(MathLink) Correct handling of Messages generated by slave kernel
Asked Answered
C

2

3

When working through MathLink with slave kernel I have a problem with correct parsing TextPackets. In particular when such packet corresponds to a Message generated by the slave kernel I do not understand how to handle it correctly at all. I need such Messages to be printed in the evaluation notebook as if they were generated by master kernel (but with some mark to make clear that it comes from the slave). And I need to separate TextPackets corresponding to Messages from just to Print[] commands. The latter I need to parse correctly too, printing them in the evaluation notebook with a little mark that it is from the slave kernel.

Here is an example of what happens:

link = LinkLaunch[First[$CommandLine] <> " -mathlink"]
Print@LinkRead[link]
LinkWrite[link, 
 Unevaluated[EnterExpressionPacket[Print[a]; 1/0; Print[b]]]]
While[Not@MatchQ[packet = LinkRead[link], InputNamePacket[_]], 
 Print[packet]]

The Message by default comes through MathLink in the form:

TextPacket[                                 1
Power::infy: Infinite expression - encountered.
                                 0]

It looks ugly. The only way to make it better I have found is to evaluate in the slave kernel

$MessagePrePrint = InputForm;

But I think there should be more straightforward solution. In particular when dealing this way I get TextPackets with HoldForms inside:

TextPacket[Power::infy: Infinite expression HoldForm[0^(-1)] encountered.]

I do not know how to convert such string into a form appropriate for printing as a Message.

P.S. This question comes from that question.

Chart answered 14/2, 2011 at 23:33 Comment(0)
C
4

I would like to share a nice hack proposed by Todd Gayley (Wolfram Research) in connection with the given question. Perhaps for somebody it will be useful as also for me. This hack solves the problem in question in rather elegant way.

One technique is to leave the FormatType at OutputForm for computations, but override the handling of Message to temporarily switch to StandardForm, so that only Message output comes back in StandardForm:

LinkWrite[link,
        Unevaluated[EnterExpressionPacket[
            Unprotect[Message];
            Message[args___]:=
               Block[{$inMsg = True, result},
                  SetOptions[$Output, FormatType->StandardForm];
                  result = Message[args];
                  SetOptions[$Output, FormatType->OutputForm];
                  result
               ] /; !TrueQ[$inMsg]
           ]
        ]]

You will get back an ExpressionPacket for the content of a message. To print that as a Message cell in the notebook:

cell = Cell[<the ExpressionPacket>, "Message", "MSG"]
CellPrint[cell]

Advanced approach: everything is printed in the StandardForm

For having everything except output returned in StandardForm we could redefine variables $Pre and $Post in the slave kernel in a special way (the following code should be evaluated in the slave kernel):

SetOptions[$Output, {PageWidth -> 72, FormatType -> StandardForm}];
(*$inPost is needed for tracing mode compatibility 
(could be switched on by evaluating On[] in the slave kernel) 
in which Messages are printed during evaluation of $Post.*)
$inPost = False; Protect[$inPost];
$Pre := Function[inputexpr, 
  SetOptions[$Output, FormatType -> StandardForm]; 
  Unevaluated[inputexpr], HoldAllComplete];
$Post := Function[outputexpr, 
  Block[{$inPost = True}, 
   SetOptions[$Output, FormatType -> OutputForm]; 
   Unevaluated[outputexpr]], HoldAllComplete];
Protect[$Pre]; Protect[$Post];
$inMsg = False; Protect[$inMsg];
Unprotect[Message];
Message[args___] /; $inPost := Block[{$inMsg = True},
    SetOptions[$Output, FormatType -> StandardForm];
    Message[args];
    SetOptions[$Output, FormatType -> OutputForm]] /; ! $inMsg;
Protect[Message];
Chart answered 1/3, 2011 at 1:25 Comment(0)
P
2

The expression comes in HoldForm always, but with the default $MessagePrePrint it is not rendered. Try evaluating

HoldForm[1/0]

InputForm[%]

One way to achieve your desired behavior would be to implement your own box renderer. To see that the renderer has to process, set

$MessagePrePrint = ToBoxes[{##}] &

in the slave. Like so:

link = LinkLaunch[First[$CommandLine] <> " -mathlink"]
Print@LinkRead[link]
LinkWrite[link, 
 Unevaluated[
  EnterExpressionPacket[$MessagePrePrint = ToBoxes[{##}] &; Print[a]; 
   1/0; Print[b]]]]
While[Not@MatchQ[packet = LinkRead[link], InputNamePacket[_]], 
 Print[packet]]
Perforate answered 19/2, 2011 at 17:1 Comment(4)
But is there a better way than implementing additional box renderer?Chart
How about using ToString and process the result, like so $MessagePrePrint = StringReplace[ToString[InputForm[#]], "HoldForm[" ~~ Shortest[x___] ~~ "]" :> x] &Perforate
It is simpler but subscripts, underscripts and named characters looks ugly in the InputForm. I hoped there is some "magic" function that would force the slave kernel to send messages in convenient form (for example as entire Cell content without need to render it further). If the idea of creation of the box renderer has no alternative I need some help with it. For example I do not understand why this generates error: ToExpression[ToString[ToBoxes[HoldForm[Infinity - Infinity]]]] (it is from message generated for input Infinity - Infinity).Chart
@AlexeyPopkov The problem is that ToString is using OutputForm by default, which is guaranteed to work with ToExpression, you either need StandardForm, or InputForm. Like ToExpression[ ToString[ToBoxes[HoldForm[Infinity - Infinity]], InputForm]].Perforate

© 2022 - 2024 — McMap. All rights reserved.