# Prepend Information to Warning Messages

I have a function running within a Do loop that sometimes issues a warning. I'd like to prepend the warning with the loop ctr so that I can go back and debug that instance later.
Basically, I would like to modify the following line,

Do[i^0, {i, -1, 1}]


so that instead of displaying the warning:

Power::indet: Indeterminate expression 0^0 encountered. >>


it displays:

i=0, Power::indet: Indeterminate expression 0^0 encountered. >>


Where i==0 is the iteration that i^0 issues the warning.

Thanks

-

Here is my proposal for tagging messages with (the value of) an arbitrary expression at the time of message generation. The tag is placed inside the the message itself.

ClearAll[withTaggedMsg]
SetAttributes[withTaggedMsg, HoldAll]

withTaggedMsg[exp_, label_: "When"] := Function[,
InternalInheritedBlock[{MessagePacket},
Unprotect @ MessagePacket;
mp : MessagePacket[__, _BoxData] /; !TrueQ[$tagMsg] := Block[{$tagMsg = True},
Style[Row[{label, HoldForm[exp], "=", exp, " "}, "  "], "SBO"] /. tag_ :>
MapAt[RowBox[{ToBoxes @ tag, #}] &, mp, {-1, 1}]
];
#
],
HoldAll]


Usage:

Do[i^0, {i, -1, 1}] // withTaggedMsg[i]


Do[i^0, {i, -1, 1}] // withTaggedMsg[i, "At iteration"]


Note: this only works with variables that are either globally accessible or are scoped using Block. For example,

f[x_] := Message[f::brains, x]
f[5] // withTaggedMsg[x]
(* At iteration x = x f::brains: -- Message text not found -- (5) *)

Module[{x = 5},
Message[f::brains, x]
] // withTaggedMsg[x]
(* At iteration x = x f::brains: -- Message text not found -- (5) *)

With[{x = 5},
Message[f::brains, x]
] // withTaggedMsg[x]
(* At iteration x = x f::brains: -- Message text not found -- (5) *)

Block[{x = 5},
Message[f::brains, x]
] // withTaggedMsg[x]
(* At iteration x = 5 f::brains: -- Message text not found -- (5) *)


This means that any variable that is scoped using Block can be used to tag a message. So, loop variables from Do and Table are accessible via this method, in addition to any Block variable. This makes it indispensable as a debugging tool.

-
Argh, I hadn't thought about what happens to the other fields. I do like your second method better, though; it is more readable and closer to what the OP wants. Although, the two cell output isn't to my liking, all that much, it is very nice, overall. +1 –  rcollyer Jul 12 '12 at 0:54
I added a fix per Rojo. –  rcollyer Jul 13 '12 at 14:23
@rcollyer thanks! –  Mr.Wizard Jul 13 '12 at 15:10
This is why I participate here: I'm having to make use of this to debug some code to load a file, and this allows me to spit out the line numbers with the errors. I'd give you another +1 if I could. –  rcollyer Oct 11 '12 at 18:34
@rcollyer Glad to be of help, as always. Thanks for letting me know, I value that a lot more than points. –  Mr.Wizard Oct 11 '12 at 19:06

I cannot seem to make it do exactly what you want do to how messages are created, but here is a serviceable alternative using $MessagePrePrint. $MessagePrePrint formats the variables specified in the message string, and in your example, the message has the form

General::indet = "Indeterminate expression 1 encountered."


where the 1 will be replaced by $0^0$, or whatever else you pass to it. It is that argument that $MessagePrePrint operates on, and we can change it to suit us, as follows Block[{$MessagePrePrint},
$MessagePrePrint := ToString[StandardForm[#]] <> " at i = " <> ToString[i] &; Do[i^0, {i, -1, 1}] ]  where the output will now read "$0^0\text{ at i} =1$". (Edit from Mr.Wizard) The code above fails in Mathematica 7. It seems that v7 doesn't like the fact that InputForm @ ToString @ StandardForm @ HoldForm[0^0]   "\!$$\*TagBox[\(0\^0$$, HoldForm]\)"  Here is a variation that is compatible with version 7: Block[{$MessagePrePrint},
$MessagePrePrint := Row@{#, " at i = ", i} ~ToString~ StandardForm &; Do[i^0, {i, -1, 1}] ]  This is still not ideal however, as every field in the message gets this tag. For example, if you enter the spurious Inner[f, {{1, 2}}, {3}] you get: Inner::incom: Length 2 at i = 1 of dimension 2 at i = 1 in {{1,2}} at i = 1 is incommensurate with length 1 at i = 1 of dimension 1 in {3} at i = 1. >> - It seems that v7 doesn't like the fact that InputForm@ToString@StandardForm@HoldForm[0^0] yields "\!$$\*TagBox[\(0\^0$$, HoldForm]\)". Either the form I show in my answer or ToBoxes[ToString[StandardForm@#] <> " at i = " <> ToString[i]] & appear to work. – Mr.Wizard Jul 11 '12 at 22:25 add comment rcollyer has a nice solution. Here's another possibility using Check and printing the list of messages generated at the current evaluation. Quiet@Block[{$OldMessages = 0},
Do[Check[#^#/# &@Mod[i, 2],
Print@StringForm["At i=, ", i, $MessageList[[$OldMessages + 1 ;;]]];
$OldMessages = Length@$MessageList;],
{i, 0, 5}]
];

(* At i=0, {Power::indet,Power::infy}
At i=2, {Power::indet,Power::infy}
At i=4, {Power::indet,General::stop,Power::infy,General::stop} *)


Remove the Quiet@` if you want to see the actual messages generated.

-