How to abort on any message generated?
I found a robust solution described in this MathGroup message by Maxim Rytin:
messageHandler = If[Last[#], Abort[]] &
Internal`AddHandler["Message", messageHandler]
This will abort the computation whenever a message would be printed.
It can be turned off using
Internal`RemoveHandler["Message", messageHandler]
Alternatively this can be temporarily applied to a piece of code like this:
Internal`HandlerBlock[
{"Message", messageHandler},
(* ... code here ... *)
]
The currently set message handlers can be retrieved using
Internal`Handlers["Message"]
(Internal`Handlers[]
will return all existing handlers)
Whenever a message is generated, Hold[message, printed]
is passed to all "Message" handler functions where message
is the message text and printed
is True is the message would be printed, False
otherwise.
The debugging palette appears to use the same mechanism to break on messages.
To make it work with parallel evaluations, one can simply register the same handler on all kernels using ParallelEvaluate
.
What about something like this?
Function[i, {i, ParallelEvaluate[i]};, HoldFirst][
Unprotect[Message, Check, Quiet];
Module[{$guardMes = True, $guardChck = True, $guardQuiet = True},
Message[args___ /; $guardMes] := Block[{$guardMes = False},
Message[args];
If[Head[First@{args}] =!= $Off, Abort[]];
];
Quiet[args___ /; $guardQuiet] := Block[{$guardQuiet = False, $guardMes = False},
Quiet[args]
];
Check[args___ /; $guardChck] := Block[{$guardChck = False, $guardMes = False},
Check[args]
];
]
]
EDIT to try to reflect Szabolcs's and Sjoerd's concerns
Function[i, {i, ParallelEvaluate[i]};, HoldFirst][
Module[{$guardMes = True},
OnMessageAbort[] := (
Unprotect[Message];
Message[args___ /; $guardMes && Head[First@{args}] =!= $Off] :=
Block[{$guardMes = False, flag = False},
Internal`InheritedBlock[{$MessagePrePrint},
$MessagePrePrint =
With[{fn = $MessagePrePrint},
Function[i, flag = True; fn[i], HoldAll]];
Message[args];
If[flag, Abort[];]
]
];
Protect[Message]);
OffMessageAbort[] := (
Unprotect[Message];
Quiet[
Message[args___ /; $guardMes && Head[First@{args}] =!= $Off] =.];
Protect[Message];
)
];
];
OnMessageAbort[ker_] := ParallelEvaluate[OnMessageAbort[], ker];
OnMessageAbort[All] := (ParallelEvaluate[OnMessageAbort[]];
OnMessageAbort[]);
OffMessageAbort[ker_] := ParallelEvaluate[OffMessageAbort[], ker];
OffMessageAbort[All] := (ParallelEvaluate[OffMessageAbort[]];
OffMessageAbort[]);
You run that once, and it defines (On/Off)MessageAbort[] in all kernels. You can call OnMessageAbort[] to turn it on on the kernel you run that, or OnMessageAbort[kernel] from the main kernel to turn it on on a particular kernel, just like the second argument to ParallelEvaluate. OnMessageAbort[All] turns it on in all of them. Same goes for OffMessageAbort
The easiest and, so far, the best solution I have found is the following:
(* Put the following two lines at the top of every notebook. *)
messageHandler = If[Last[#], Interrupt[]] & ;
Internal`AddHandler["Message", messageHandler];
The above code is slightly modified from Szabolcs's solution at the beginning of this thread. I changed Abort
to Interrupt
. The latter seems to work immediately, while it appears that Abort
may be waiting for some portion of the evaluation to finish before stopping everything.