RE: Full text of the current warning message(s)
- To: mathgroup at smc.vnet.net
- Subject: [mg33405] RE: [mg33389] Full text of the current warning message(s)
- From: "Wolf, Hartmut" <Hartmut.Wolf at t-systems.com>
- Date: Wed, 20 Mar 2002 01:53:12 -0500 (EST)
- Sender: owner-wri-mathgroup at wolfram.com
> -----Original Message----- > From: Vladimir Bondarenko [mailto:vvb at mail.strace.net] To: mathgroup at smc.vnet.net > Sent: Tuesday, March 19, 2002 5:39 AM > To: mathgroup at smc.vnet.net > Subject: [mg33405] [mg33389] Full text of the current warning message(s) > > > $MessageList is a global variable that gives a *list* of the > names of messages > generated by Mathematica during the evaluation of the current > input line. > > How can I get the WHOLE text of the warning message(s)? (not > only a template > like "Expression `1` contains`2`" which are stored in Message.m) ? > > For example, NSum[1, {n, 1, foo}] generates the following > warning message > > NSum::"nslim": "Limit of summation foo is not a number." > > > What is the simplest way to store this string into a variable? > > > Vladimir Bondarenko > > > Vladimir, one idea would be to write the error messages to a file, and retrieve them when needed: In[1]:= Attributes[saveMessages] = {HoldAllComplete}; In[59]:= saveMessages[expression_, append_:True] := Block[{$Messages = If[append, OpenAppend["thoseMessages"], OpenWrite["thoseMessages"]], x}, Write[$Messages, Line[$Line]]; (* current line no *) (* begin of your code *) x = expression; (* end of your code *) Write[$Messages]; (* an empty line *) Close[$Messages]; x] In[66]:= recallMessages[] := Block[{s = OpenRead["thoseMessages"]}, Fold[If[#2 === "", Print[StringDrop[#1, -1]]; "", #1 <> #2 <> "\n"] &, "", ReadList["thoseMessages", String]]; Close[s];] In[71]:= saveMessages[ Exp[a, b, c]; Log[-1/0, a], False] Out[71]= 0 In[72]:= saveMessages[ Log[a, b, c]; Exp[-1/0, a] ] Out[72]= Exp[ComplexInfinity, a] In[78]:= recallMessages[] >From In[78]:= "Line[71]" >From In[78]:= "Exp::argx: Exp called with 3 arguments; 1 argument is expected." >From In[78]:= " 1\n Power::infy: Infinite expression - encountered.\n 0" >From In[78]:= "Line[72]" >From In[78]:= "Log::argt: Log called with 3 arguments; 1 or 2 arguments are expected." >From In[78]:= " 1\n Power::infy: Infinite expression - encountered.\n 0" >From In[78]:= "Exp::argx: Exp called with 2 arguments; 1 argument is expected." In[79]:= saveMessages[Sqrt[a, b, c], False] Out[79]= Sqrt[a, b, c] In[80]:= recallMessages[] >From In[80]:= "Line[79]" >From In[80]:= "Sqrt::argx: Sqrt called with 3 arguments; 1 argument is expected." (I edited the output a little bit, as to indicate the actual printed representation.) Of course you may recall the messages differently, but they are essentially in a printed format as seen. Here another, more complicated idea, yet, perhaps with a better result: the messages are retrieved in there original form: In[63]:= Attributes[storeMessages] = {HoldAllComplete, SequenceHold} Out[63]= {HoldAllComplete, SequenceHold} In[64]:= storeMessages[expression_] := Block[{$MessagePrePrint = Block[{m = Short[#]}, AppendTo[messageArgs, m]; m] &}, messageArgs = {}; $Post := (messagesSaved = $MessageList; $Post =.; #) &; expression] $MessagePrePrint accumulates the expressions that were ##2 arguments to Message. $Post saves $MessageList and immediately resets itself, as not overwrite the saved message list when evaluating the next expression. This was necessary since just localizing $Post within Block is of no avail, because $Post is only called at the top-level of the main evaluation loop, and such not within Block. In[65]:= storeMessages[Log[a, b, c]; Exp[-1/0, 10]] >From In[65]:= Log::"argt": "Log called with 3 arguments; 1 or 2 arguments are expected." >From In[65]:= Power::"infy": "Infinite expression 1/0 encountered." >From In[65]:= Exp::"argx": "Exp called with 2 arguments; 1 argument is expected." Out[65]= Exp[ComplexInfinity, 10] In[66]:= messagesSaved Out[66]= {Log::"argt", Power::"infy", Exp::"argx"} In[67]:= messageArgs // InputForm Out[67]//InputForm= {Short[HoldForm[Log]], Short[HoldForm[3]], Short[HoldForm[1]], Short[HoldForm[2]], Short[HoldForm[0^(-1)]], Short[HoldForm[Exp]], Short[HoldForm[2]]} The messages and the arguments have been saved In[68]:= retrieveMessages[] := Module[{rawmsg, rawpos, posns, partargs}, Block[{$Messages = OpenWrite["tmp"]}, Message @@@ messagesSaved; rawmsg = ReadList[OpenRead["tmp"], String]; Close /@ Streams["tmp"]]; rawpos = Length /@ (StringPosition[#, "`"] &) /@ rawmsg; posns = Rest[FoldList[{#1[[2]] + 1, #1[[2]] + #2} &, {0, 0}, Last[Transpose[Partition[rawpos, 2]]]/2]]; partargs = Take[messageArgs, #] & /@ posns; Message @@@ MapThread[Join, {messagesSaved, HoldForm @@@ partargs}]; ] In[69]:= retrieveMessages[] >From In[69]:= Log::"argt": "Log called with 3 arguments; 1 or 2 arguments are expected." >From In[69]:= Power::"infy": "Infinite expression 1/0 encountered." >From In[69]:= Exp::"argx": "Exp called with 2 arguments; 1 argument is expected." The error messages have been retrieved in full blossom. Do it again! In[72]:= storeMessages[Sqrt[a, b, c]] >From In[72]:= Sqrt::"argx": "Sqrt called with 3 arguments; 1 argument is expected." Out[72]= Sqrt[a, b, c] In[73]:= retrieveMessages[] >From In[73]:= Sqrt::"argx": "Sqrt called with 3 arguments; 1 argument is expected." Retrieve Messages is a little bit complicated though. This all was neccessary since I found no way to splice in a delimiter between the arguments of consecutive error messages. (Have you got an idea? tell!) So I had to count the number of arguments needed. For that I looked at the control string for the first StringForm parameter of Message. Normally it would suffice to evaluate i.e. Power::"infy" by putting off the wrapper HoldForm. This did not work for Log::"argt" and Exp::"argx", since evidently those messages are not stored with MessageName[...]; obviously they are treated in a special way within Message to extract "Log" and "Exp" as paramaters for a message unknown to me (couldn't get at it). So I needed Message again to extract that raw form. This is done with Message @@@ messagesSaved . Again we intercepted the message stream. (It certainly is a bit nasty to do that via a file "tmp"; I would have prefered something like StringToStream to write to; found no way however.) The next two lines are more easy: rawpos is (twice) the number of arguments expected within every message. Also there is an empty line preceeding each raw message; parargs then gives the ranges for the corresponding actual parameters in messagesSaved. As told, message has to be applied directly to its unevaluated (first) argument. So I Joined those with the corresponding actual arguments. To achieve that, they had to be wrapped with HoldForm (as the "message names" were). Perhaps this could be done more easily; I'd be eager to know. -- Hartmut Wolf