9

Is there a setting (I'd expect it to be an environment variable) in Mathematica that would make the kernel quit at the occurrence of a debug error? Often, I'll get debug errors and the notebook will continue evaluating bad data forcing me to restart the kernel.

Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
rjkaplan
  • 3,138
  • 5
  • 27
  • 33
  • 2
    What kind of 'debug' error are you referring to? – Sjoerd C. de Vries Nov 02 '11 at 21:06
  • I'm afraid I don't know enough to answer that question intelligently. Do you have some reference, or just a suggestion as to what I should search for in the documentation, to learn more about debug messages? – rjkaplan Nov 02 '11 at 21:42
  • Find the very first message emitted, the message name has the form `symbol::msgname`. Post that, if you would. – rcollyer Nov 03 '11 at 00:16
  • @rjkaplan What you call "debug errors", do they appear in a separate window with pink background, or do they appear in your notebook and look like "SomeSymbol::msg description"? – Szabolcs Nov 03 '11 at 21:28
  • They appear in the notebook and look like "SomeSymbol::msg description". Primarily, I'd like my notebook to stop running upon an Assertion error. – rjkaplan Nov 03 '11 at 22:00

4 Answers4

14

Following an idea borrowed from the Mathematica toolbag:

myMessage::debug = "Something went horribly wrong";

Unprotect[Message];
Message[mm : myMessage::debug] :=
 Block[{$inMsg = True, result},
   Message[mm];
   Quit[]] /; ! TrueQ[$inMsg]
Protect[Message];

Here, replace myMessage::debug with the actual message type you want to intercept.

Normal message:

Message[Power::infy]

(* ===> 
        StringForm::sfr: Item 1 requested in "Infinite expression `1` 
        encountered. >>" out of range; 0 items available. >> 

        Power::infy: Infinite expression `1` encountered. >> 
*)

Your 'debug' message:

Message[myMessage::debug]

(* ===>
myMessage::debug: Something went horribly wrong

 [kernel quits...]
*)

Update

Assert messages get some additional arguments, so you have to catch those too. And, by the way, you have to put the actual message name in the definition (not using the example message I used above):

Unprotect[Message];
Message[mm : Assert::asrtfl, m___] :=
 Block[{$inMsg = True, result},
   Message[mm, m];
   Quit[]] /; ! TrueQ[$inMsg]
Protect[Message];
Community
  • 1
  • 1
Sjoerd C. de Vries
  • 16,122
  • 3
  • 42
  • 94
  • Thank you for the response! I have a confusing problem, though: when I set myMessage::debug = "Assert::asrtfl" and send a Message["Assert::asrtfl:"], the assertion failure is caught and the kernel exits. But when an actual assertion happens, it isn't caught. Am I wrong that the message of an Assertion fail is "Assert::asrtfl"? – rjkaplan Nov 03 '11 at 22:23
  • Thank you again for the response, but copying and pasting the code you give in the update still doesn't catch Asserts for some reason. Could I confirm with you that you tested it? – rjkaplan Nov 04 '11 at 00:25
  • @rjkaplan Yes, I did. I used the example code on the Assert doc page. ``<< ExampleData`FunctionWithAssert` On[Assert]; Compute[1.0]`` – Sjoerd C. de Vries Nov 04 '11 at 09:10
8

This response assumes that by "debug error" you mean that a Message has been issued. For example, a message is issued as a warning if one attempts to divide by zero:

In[1]:= Print[1/0]

        Power::infy: Infinite expression 1/0 encountered. >>

        ComplexInfinity

Note how ComplexInfinity was printed by the Print statement even though the warning message was issued, showing that the computation continues after the "error".

I will not re-iterate @Sjoerd's excellent answer which shows how to configure a Mathematica session so that the kernel will exit if any message is issued by any evaluation. If you wish to be more selective and only quit the kernel if a particular evaluation issues a message, then the following function might be useful:

ClearAll[checkQuit]
SetAttributes[checkQuit, HoldFirst]
checkQuit[expr_, HoldPattern[messages_:Sequence[]]] :=
  Check[expr, Message[checkQuit::quit]; Quit[], messages]
checkQuit::quit = "The kernel is being shut down!";

With this definition in place, you can force the kernel to quit after any message is issued by a particular evaluation:

In[37]:= checkQuit[Print[1/0]]

         Power::infy: Infinite expression 1/0 encountered. >>
         checkQuit::quit: The kernel is being shut down!

If might be convenient to only quit the kernel if certain messages are issued. To this end, checkQuit accepts an optional second argument that can be used to specify the messages of interest:

In[6]:= checkQuit[{}[[10]], Power::infy]

        Part::partw: Part 10 of {} does not exist. >>
Out[6]= {}[[10]]

Note how the kernel was not exited since the message did not match Power::infy. But consider:

In[7]:= checkQuit[1/0, Power::infy]

        Power::infy: Infinite expression 1/0 encountered. >>
        checkQuit::quit: The kernel is being shut down!

Here the kernel quits because the indicated message appeared. It is possible to filter multiple messages or even pre-defined groups of messages -- see Check for details.

WReach
  • 18,098
  • 3
  • 49
  • 93
7

In addition to the other answers you may want to investigate using:

$MessagePrePrint

Which allows you to execute code when any message is triggered.

For example, you can evaluate this:

$MessagePrePrint := (Print[Stack[_]]; Dialog[])

Which prints the evaluation stack (sometimes very large!) of the code that is evaluating. The dialog function then puts you in a sub-evaluation loop, allowing you to inspect the values of variables. To get out of the dialog loop, you can evaluate:

Return[]

which takes you back to the main loop (the main evaluation then either finishes or breaks at a new message).

Arnoud Buzing
  • 15,383
  • 3
  • 20
  • 50
5

You wrote:

Primarily, I'd like my notebook to stop running upon an Assertion error.

You can do this by defining $AssertFunction as follows:

$AssertFunction := Quit[] & ;

Now the kernel quits when an assertion fails:

<< ExampleData`FunctionWithAssert` 
Compute[ 1.0]

(*=> kernel quits*)

Alternatively, you can use Interrupt in the same way:

$AssertFunction := Interrupt[] & ;

It allows you to abort the evaluation or enter subsession (the same as Dialog[] does in Arnoud's answer).

You also can extend this approach for printing the assertion which failed:

$AssertFunction := (Print[HoldForm @@ #]; Interrupt[]) &;
Alexey Popkov
  • 9,355
  • 4
  • 42
  • 93