Add support for ending a jupyter-console session when Quit[___] or Exit[___] are evaluated, or when the code strings "Quit", "Exit", "quit", or "exit" exactly are sent over by the console

This commit is contained in:
cc-wr 2019-12-05 08:09:05 -05:00
parent 7bc5ee8ae6
commit e006c4f7e1
4 changed files with 117 additions and 62 deletions

View File

@ -77,21 +77,55 @@ If[
Print[ourArgs___, opts:OptionsPattern[]] :=
Block[
{
$inPrint=True,
$Output={OpenWrite[FormatType->OutputForm]}
$inPrint = True,
$Output
},
If[
!FailureQ[First[$Output]],
loopState["printFunction"] =!= False,
$Output = {OpenWrite[FormatType -> OutputForm]};
If[
!FailureQ[First[$Output]],
Print[ourArgs, opts];
loopState["printFunction"][
Import[$Output[[1,1]], "String"]
];
Close[First[$Output]];
];
];
Close[First[$Output]];
Null
] /; !TrueQ[$inPrint];
Protect[Print];
(************************************
versions of Quit and Exit that
ask the Jupyter console
to quit, if running under
a Jupyter console
*************************************)
Unprotect[Quit];
Quit[ourArgs___, opts:OptionsPattern[]] :=
Block[
{$inQuit = True},
If[
loopState["isCompleteRequestSent"],
loopState["askExit"] = True;,
Quit[ourArgs, opts];
];
] /; !TrueQ[$inQuit];
Protect[Quit];
Unprotect[Exit];
Exit[ourArgs___, opts:OptionsPattern[]] :=
Block[
{$inExit = True},
If[
loopState["isCompleteRequestSent"],
loopState["askExit"] = True;,
Exit[ourArgs, opts];
];
] /; !TrueQ[$inExit];
Protect[Exit];
(************************************
versions of Throw and
Catch that we can
@ -119,7 +153,7 @@ If[
*************************************)
(* redirect Print to Jupyter *)
redirectPrint[sourceFrame_, printText_] :=
redirectPrint[currentSourceFrame_, printText_] :=
(* send a frame *)
sendFrame[
(* on the IO Publish socket *)

View File

@ -201,8 +201,11 @@ If[
"replyContent" -> Null,
(* message relpy frame to send on the IO Publish socket, if it is not Null *)
"ioPubReplyFrame" -> Null,
(* the function Print should use *)
"printFunction" -> Function[#;]
(* the redirect function Print should use *)
"printFunction" -> False,
(* flag for if the Jupyter console (if running under a Jupyter console)
should ask the user if it should exit *)
"askExit" -> False
];
(* helper utility for applying hooks if they are set *)
@ -232,7 +235,7 @@ If[
bannerWarning =
If[
Length[$CommandLine] > 4,
"\\n\\nThis Jupyter kernel was installed through the WolframLanguageForJupyter WolframScript script install option. Accordingly, updates to a WolframLanguageForJupyter paclet installed to a Wolfram Engine will not propagate to this installation.",
"\\n\\nNote: This Jupyter kernel was installed through the WolframScript install method. Accordingly, updates to a WolframLanguageForJupyter paclet will not affect this kernel.",
""
];

View File

@ -31,7 +31,7 @@ Needs["ZeroMQLink`"]; (* SocketReadMessage *)
files
*************************************)
Get[FileNameJoin[{DirectoryName[$InputFileName], "Initialization.wl"}]]; (* initialize WolframLanguageForJupyter; loopState, bannerWarning, shellSocket, ioPubSocket *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "Initialization.wl"}]]; (* initialize WolframLanguageForJupyter; loopState, bannerWarning, shellSocket, controlSocket, ioPubSocket *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "SocketUtilities.wl"}]]; (* sendFrame *)
Get[FileNameJoin[{DirectoryName[$InputFileName], "MessagingUtilities.wl"}]]; (* getFrameAssoc, createReplyFrame *)
@ -49,24 +49,27 @@ Begin["`Private`"];
loop[] :=
Module[
{
(* the socket that has become ready *)
readySocket,
(* the raw byte array frame received through SocketReadMessage *)
rawFrame,
(* a frame for sending status updates on the IO Publish socket *)
statusReplyFrame,
(* a frame for sending replies on the shell socket *)
shellReplyFrame
(* a frame for sending replies on a socket *)
replyFrame
},
While[
True,
Switch[
(* poll sockets until one is ready *)
First[SocketWaitNext[{shellSocket}]],
(* if the shell socket is ready, ... *)
shellSocket,
readySocket = First[SocketWaitNext[{shellSocket, controlSocket}]],
(* if the shell socket or control socket is ready, ... *)
shellSocket | controlSocket,
(* receive a frame *)
rawFrame = SocketReadMessage[shellSocket, "Multipart" -> True];
rawFrame = SocketReadMessage[readySocket, "Multipart" -> True];
(* check for any problems *)
If[FailureQ[rawFrame],
Quit[];
@ -123,8 +126,8 @@ loop[] :=
(* send the frame *)
sendFrame[ioPubSocket, statusReplyFrame];
(* create a message frame to send a reply on the shell socket *)
shellReplyFrame =
(* create a message frame to send a reply on the socket that became ready *)
replyFrame =
createReplyFrame[
(* use the current source frame *)
loopState["frameAssoc"],
@ -136,7 +139,7 @@ loop[] :=
False
];
(* send the frame *)
sendFrame[shellSocket, shellReplyFrame];
sendFrame[readySocket, replyFrame];
(* if an ioPubReplyFrame was created, send it on the IO Publish socket *)
If[
@ -161,10 +164,10 @@ loop[] :=
]
];
(* if the doShutdown flag was set as True, shut down*)
(* if the doShutdown flag is True, shut down *)
If[
loopState["doShutdown"],
Quit[];
Block[{$inQuit = True}, Quit[]];
];
,
_,

View File

@ -118,31 +118,21 @@ If[
unreportedErrorMessages
},
(* set the appropriate reply type *)
loopState["replyMsgType"] = "execute_reply";
(* set the content of the reply to information about WolframLanguageForJupyter's execution of the input *)
loopState["replyContent"] =
ExportString[
Association[
"status" -> "ok",
"execution_count" -> loopState["executionCount"],
"user_expressions" -> {}
],
"JSON",
"Compact" -> True
];
(* redirect Print so that it prints in the Jupyter notebook *)
loopState["printFunction"] = (redirectPrint[loopState["frameAssoc"], #1] &);
(* if an is_complete_request has been sent, assume jupyter-console is running the kernel,
and redirect messages *)
redirect messages, and handle any "Quit", "Exit", "quit" or "exit" inputs *)
If[
loopState["isCompleteRequestSent"],
loopState["redirectMessages"] = True;
loopState["askExit"] =
StringMatchQ[
loopState["frameAssoc"]["content"]["code"],
"Quit" | "Exit" | "quit" | "exit"
];
];
(* redirect Print so that it prints in the Jupyter notebook *)
loopState["printFunction"] = (redirectPrint[loopState["frameAssoc"], #1] &);
(* if loopState["redirectMessages"] is True,
update Jupyter explicitly with any errors that occur DURING the execution of the input *)
If[
@ -163,8 +153,29 @@ If[
(* evaluate the input, and store the total result in totalResult *)
totalResult = simulatedEvaluate[loopState["frameAssoc"]["content"]["code"]];
(* restore printFunction to empty *)
loopState["printFunction"] = Function[#;];
(* restore printFunction to False *)
loopState["printFunction"] = False;
(* unset Internal`$MessageFormatter *)
Unset[Internal`$MessageFormatter];
(* set the appropriate reply type *)
loopState["replyMsgType"] = "execute_reply";
(* set the content of the reply to information about WolframLanguageForJupyter's execution of the input *)
loopState["replyContent"] =
ExportString[
Association[
"status" -> "ok",
"execution_count" -> loopState["executionCount"],
"user_expressions" -> {},
(* see https://jupyter-client.readthedocs.io/en/stable/messaging.html#payloads-deprecated *)
(* if the "askExit" flag is True, add an "ask_exit" payload *)
"payload" -> If[loopState["askExit"], {Association["source" -> "ask_exit", "keepkernel" -> False]}, {}]
],
"JSON",
"Compact" -> True
];
(* check if there are any unreported error messages *)
unreportedErrorMessages =
@ -175,6 +186,30 @@ If[
(StringLength[totalResult["GeneratedMessages"]] > 0)
);
(* if there are no results, or if the "askExit" flag is True,
do not send anything on the IO Publish socket and return *)
If[
(Length[totalResult["EvaluationResultOutputLineIndices"]] == 0) ||
(loopState["askExit"]),
(* set the "askExit" flag to False *)
loopState["askExit"] = False;
(* send any unreported error messages *)
If[unreportedErrorMessages,
redirectMessages[
loopState["frameAssoc"],
"",
totalResult["GeneratedMessages"],
(* do not add a newline *)
False,
(* drop message name *)
True
];
];
(* increment loopState["executionCount"] as needed *)
loopState["executionCount"] += totalResult["ConsumedIndices"];
Return[];
];
(* generate an HTML form of the message text *)
errorMessage =
If[
@ -196,26 +231,6 @@ If[
}
];
(* if there are no results, do not send anything on the IO Publish socket and return *)
If[
Length[totalResult["EvaluationResultOutputLineIndices"]] == 0,
(* send any unreported error messages *)
If[unreportedErrorMessages,
redirectMessages[
loopState["frameAssoc"],
"",
totalResult["GeneratedMessages"],
(* do not add a newline *)
False,
(* drop message name *)
True
];
];
(* increment loopState["executionCount"] as needed *)
loopState["executionCount"] += totalResult["ConsumedIndices"];
Return[];
];
(* format output as purely text, image, or cloud interface *)
If[
(* check if the input was wrapped with Interact,