Skip to content

Commit

Permalink
add custom kernel picker
Browse files Browse the repository at this point in the history
  • Loading branch information
brettfo committed Nov 23, 2022
1 parent 883c52e commit 1f3830a
Show file tree
Hide file tree
Showing 152 changed files with 5,286 additions and 4,082 deletions.
310 changes: 310 additions & 0 deletions samples/stand-alone-kernels/perl/kernel.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
# Copyright (c) .NET Foundation and contributors. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for full license information.

# This is designed to be a stand-alone, out-of-process kernel, primarily used to ensure proper proxy
# handling. It requires a local `perl.exe` interpreter be available and can be used by executing
# the following in a notebook:
#
# #!connect stdio --kernel-name perl --command perl.exe kernel.pl

use JSON;
use Try::Tiny;

$|++; # autoflush

# run with `perl.exe kernel.pl test` to ensure the value serialization works as expected
if (length(@ARGV) > 0 && $ARGV[0] eq "test") {
test();
} else {
main(@ARGV);
}

sub test {
$scalarNumber = 4;
$scalarString = "four";
@array = (1, 2, 3);
$arrayRef = [4, 5, 6];
%hash = ("theAnswer" => 42, "pi" => 3.14159);
$hashRef = {"theAnswer" => 42, "pi" => 3.14159};

@names = ("scalarNumber", "scalarString", "array", "arrayRef", "hash", "hashRef");
@mimeTypes = ("text/plain", "application/json");
foreach my $mimeType (@mimeTypes) {
print "$mimeType:\n";
foreach my $name (@names) {
print " $name: " . getStringRepresentationOfValueName($name, $mimeType) . "\n";
}
}
}

sub main {
my $kernelHost = "pid-$$";

# the line "#!connect stdio ..." auto-appends these arguments
for (my $i = 0; $i < $#_; $i++) {
my $arg = $_[$i];
if ($arg eq "--kernel-host") {
$i++;
$kernelHost = $_[$i];
}
}

my $kernelUri = "kernel://$kernelHost/";

%suppressedValues = {};
foreach my $valueName ( keys %main:: ) {
if (!$suppressedValues{$valueName}) {
$suppressedValues{$valueName} = 1;
}
}

$kernelInfo = {
"localName" => "perl",
"languageName" => "perl",
"languageVersion" => "$^V",
"displayName" => "Perl $^V",
"uri" => $kernelUri,
"supportedKernelCommands" => [
{ "name" => "RequestKernelInfo" },
{ "name" => "RequestValue" },
{ "name" => "RequestValueInfos" },
{ "name" => "SendValue" },
{ "name" => "SubmitCode" },
{ "name" => "Quit" }
],
"supportedDirectives" => []
};

publish({
"eventType" => "KernelReady",
"event" => {},
"command" => undef,
"routingSlip" => [
$kernelUri
]
});

publish({
"eventType" => "KernelInfoProduced",
"event" => {
"kernelInfo" => $kernelInfo,
},
"command" => undef,
"routingSlip" => [
$kernelUri
]
});

while (<STDIN>) {
chomp;
try {
$envelope = decode_json($_);
$envelopeRoutingSlip = $envelope->{"routingSlip"};
push(@$envelopeRoutingSlip, $kernelUri . "?tag=arrived");
$commandType = $envelope->{'commandType'};
if ($commandType) {
$token = $envelope->{'token'};
$command = $envelope->{'command'};
$succeeded = false;
if ($commandType eq "Quit") {
#
# Quit
#
return;
} elsif ($commandType eq "RequestKernelInfo") {
#
# RequestKernelInfo
#
publish({
"eventType" => "KernelInfoProduced",
"event" => {
"kernelInfo" => $kernelInfo
},
"command" => $envelope,
"routingSlip" => [
$kernelUri
]
});
$succeeded = true;
} elsif ($commandType eq "RequestValue") {
#
# RequestValue
#
$valueName = $command->{'name'};
$mimeType = $command->{'mimeType'};
$formattedValue = getStringRepresentationOfValueName($valueName, $mimeType);
publish({
"eventType" => "ValueProduced",
"event" => {
"name" => $valueName,
"formattedValue" => {
"mimeType" => $mimeType,
"value" => $formattedValue
}
},
"command" => $envelope,
"routingSlip" => [
$kernelUri
]
});
$succeeded = true;
} elsif ($commandType eq "RequestValueInfos") {
#
# RequestValueInfos
#
my @valueInfos = ();
foreach my $valueName ( keys %main:: ) {
if (!$suppressedValues{$valueName}) {
push(@valueInfos, { "name" => "$valueName" });
}
}
publish({
"eventType" => "ValueInfosProduced",
"event" => {
"valueInfos" => \@valueInfos
},
"command" => $envelope,
"routingSlip" => [
$kernelUri
]
});
$succeeded = true;
} elsif ($commandType eq "SendValue") {
#
# SendValue
#
$formattedValue = $command->{'formattedValue'};
# if `application/json` or `text/json`
if ($formattedValue->{'mimeType'} =~ m/\/json$/) {
$valueName = $command->{'name'};
$jsonValue = $formattedValue->{'value'};
$runtimeValue = decode_json($jsonValue);
$main::{$valueName} = $runtimeValue;
$succeeded = true;
}
} elsif ($commandType eq "SubmitCode") {
#
# SubmitCode
#
$code = $command->{'code'};
$result = eval $code;
publish({
"eventType" => "ReturnValueProduced",
"event" => {
"formattedValues" => [{
"mimeType" => "text/plain",
"value" => "$result"
}],
},
"command" => $envelope,
"routingSlip" => [
$kernelUri
]
});
$succeeded = true;
} else {
$succeeded = false;
}

push(@$envelopeRoutingSlip, $kernelUri);
if ($succeeded) {
publish({
"eventType" => "CommandSucceeded",
"event" => {},
"command" => $envelope,
"routingSlip" => [
$kernelUri
]
});
} else {
publish({
"eventType" => "CommandFailed",
"event" => {
"message" => "Unknown command type: $commandType"
},
"command" => $envelope,
"routingSlip" => [
$kernelUri
]
});
}
}
$eventType = $envelope->{'eventType'};
if ($eventType) {
# TODO: respond to events
}
} catch {
print STDERR "error: $_\n";
}
}
}

sub publish {
print encode_json(\%{$_[0]}) . "\n";
}

sub getStringRepresentationOfValueName {
my $valueName = shift;
my $mimeType = shift;
my $rawValue = $main::{$valueName};
my $formattedValue;
# if `application/json` or `text/json`
if ($mimeType =~ m/\/json$/) {
my @asArray = @{getArray($rawValue)};
my %asHash = %{getHash($rawValue)};
if (@asArray) {
$rawValue = \@asArray;
}
elsif (%asHash) {
$rawValue = \%asHash;
}
elsif ( length do { no warnings "numeric"; $$rawValue & '' }) {
$rawValue = $$rawValue + 0;
}
else {
$rawValue = $$rawValue;
}

$formattedValue = encode_json($rawValue);
}
else {
# assume text/plain
my @asArray = @{getArray($rawValue)};
my %asHash = %{getHash($rawValue)};
if (@asArray) {
$formattedValue = "(" . join(", ", @asArray) . ")";
}
elsif (%asHash) {
$formattedValue = "(" . join(", ", map { "$_ => $asHash{$_}" } keys %asHash) . ")";
}
else {
$formattedValue = "" . $$rawValue;
}
}

return $formattedValue;
}

sub getArray {
my $rawValue = shift;
if (ref($$rawValue) eq "ARRAY") {
return \@$$rawValue;
}
elsif (@$rawValue) {
return \@$rawValue;
}

return undef;
}

sub getHash {
my $rawValue = shift;
if (ref($$rawValue) eq "HASH") {
return \%$$rawValue;
}
elsif (%$rawValue) {
return \%$rawValue;
}

return undef;
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ Microsoft.DotNet.Interactive.Documents
public System.Collections.Generic.List<InteractiveDocumentOutputElement> Outputs { get;}
public abstract class InteractiveDocumentOutputElement
public class KernelInfo
.ctor(System.String name, System.Collections.Generic.IReadOnlyCollection<System.String> aliases = null)
.ctor(System.String name, System.String languageName = null, System.Collections.Generic.IReadOnlyCollection<System.String> aliases = null)
public System.Collections.Generic.IReadOnlyCollection<System.String> Aliases { get;}
public System.String LanguageName { get;}
public System.String Name { get;}
public System.String ToString()
public class KernelInfoCollection, System.Collections.Generic.ICollection<KernelInfo>, System.Collections.Generic.IEnumerable<KernelInfo>, System.Collections.IEnumerable
Expand Down Expand Up @@ -77,7 +78,8 @@ Microsoft.DotNet.Interactive.Documents
public System.String Text { get;}
Microsoft.DotNet.Interactive.Documents.Jupyter
public class InputCellMetadata
.ctor(System.String language = null)
.ctor(System.String kernelName = null, System.String language = null)
public System.String KernelName { get;}
public System.String Language { get;}
public static class InteractiveDocumentExtensions
public static Microsoft.DotNet.Interactive.Documents.InteractiveDocument WithJupyterMetadata(System.String language = C#)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,12 @@ Microsoft.DotNet.Interactive
public System.Threading.Tasks.Task<Microsoft.DotNet.Interactive.Connection.ProxyKernel> ConnectProxyKernelOnDefaultConnectorAsync(System.String localName, System.Uri remoteKernelUri, System.String[] aliases = null)
public System.Void Dispose()
public class KernelInfo
.ctor(System.String localName, System.String languageName = null, System.String languageVersion = null, System.String[] aliases = null)
.ctor(System.String localName, System.String[] aliases = null)
.ctor(System.String localName, System.String languageName, System.String languageVersion, System.String[] aliases)
public System.String[] Aliases { get; set;}
public System.String LanguageName { get;}
public System.String LanguageVersion { get;}
public System.String DisplayName { get; set;}
public System.String LanguageName { get; set;}
public System.String LanguageVersion { get; set;}
public System.String LocalName { get;}
public System.Uri RemoteUri { get; set;}
public System.Collections.Generic.ICollection<KernelDirectiveInfo> SupportedDirectives { get; set;}
Expand Down Expand Up @@ -429,9 +431,9 @@ Microsoft.DotNet.Interactive.Commands
public class RequestValueInfos : KernelCommand
.ctor(System.String targetKernelName = null)
public class SendEditableCode : KernelCommand
.ctor(System.String language, System.String code, System.String targetKernelName = vscode)
.ctor(System.String kernelName, System.String code, System.String targetKernelName = vscode)
public System.String Code { get;}
public System.String Language { get;}
public System.String KernelName { get;}
public class SendValue : KernelCommand
.ctor(System.String name, System.Object value, Microsoft.DotNet.Interactive.FormattedValue formattedValue = null, System.String targetKernelName = null)
public Microsoft.DotNet.Interactive.FormattedValue FormattedValue { get;}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Microsoft.DotNet.Interactive.Jupyter
public System.Threading.Tasks.Task Done()
public T GetRequestContent<T>()
public static class JupyterRequestContextExtensions
public static System.String GetLanguage()
public static System.String GetKernelName()
public class JupyterRequestContextHandler
.ctor(Microsoft.DotNet.Interactive.Kernel kernel)
public System.Threading.Tasks.Task Handle(JupyterRequestContext context)
Expand Down
5 changes: 4 additions & 1 deletion src/Microsoft.DotNet.Interactive.CSharp/CSharpKernel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,11 @@ public CSharpKernel() : this(DefaultKernelName)
{

}
public CSharpKernel(string name) : base(name, "C#", "11.0")
public CSharpKernel(string name) : base(name)
{
KernelInfo.LanguageName = "C#";
KernelInfo.LanguageVersion = "11.0";
KernelInfo.DisplayName = "C# Script";
_workspace = new InteractiveWorkspace();

//For the VSCode-Add-In Directory.GetCurrentDirectory() would here return something like: c:\Users\<username>\AppData\Roaming\Code\User\globalStorage\ms-dotnettools.dotnet-interactive-vscode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ public static void RegisterEventsAndCommands()
}
}

public CSharpProjectKernel(string name = "csharp") : base(name, "C#", languageVersion:"11.0")
public CSharpProjectKernel(string name = "csharp") : base(name)
{
KernelInfo.LanguageName = "C#";
KernelInfo.LanguageVersion = "11.0";
}

async Task IKernelCommandHandler<OpenProject>.HandleAsync(OpenProject command, KernelInvocationContext context)
Expand Down
Loading

0 comments on commit 1f3830a

Please sign in to comment.