diff --git a/src/Microsoft.DotNet.Interactive.ApiCompatibility.Tests/ApiCompatibilityTests.Document_api_is_not_changed.approved.txt b/src/Microsoft.DotNet.Interactive.ApiCompatibility.Tests/ApiCompatibilityTests.Document_api_is_not_changed.approved.txt index b42b37f35c..b2b7a3bdf0 100644 --- a/src/Microsoft.DotNet.Interactive.ApiCompatibility.Tests/ApiCompatibilityTests.Document_api_is_not_changed.approved.txt +++ b/src/Microsoft.DotNet.Interactive.ApiCompatibility.Tests/ApiCompatibilityTests.Document_api_is_not_changed.approved.txt @@ -31,6 +31,7 @@ Microsoft.DotNet.Interactive.Documents public System.Collections.Generic.IAsyncEnumerable GetImportsAsync(System.Boolean recursive = False) public System.Collections.Generic.IEnumerable GetInputFields() public System.Collections.Generic.IEnumerable GetMagicCommandLines() + public System.Boolean TryGetKernelInfosFromMetadata(ref KernelInfoCollection& kernelInfos) public class InteractiveDocumentElement .ctor() .ctor(System.String contents = null, System.String kernelName = null, System.Collections.Generic.IEnumerable outputs = null) @@ -60,14 +61,13 @@ Microsoft.DotNet.Interactive.Documents public System.Boolean Contains(System.String nameOrAlias) public System.Void CopyTo(KernelInfo[] array, System.Int32 arrayIndex) public System.Boolean Remove(KernelInfo item) - public System.Boolean TryGetByAlias(System.String alias, ref KernelInfo& info) public class ReturnValueElement : InteractiveDocumentOutputElement, IDataElement - .ctor(System.Collections.Generic.IDictionary data) + .ctor() public System.Collections.Generic.IDictionary Data { get;} public System.Int32 ExecutionOrder { get; set;} public System.Collections.Generic.IDictionary Metadata { get;} public class TextElement : InteractiveDocumentOutputElement - .ctor(System.String text, System.String name) + .ctor(System.String text, System.String name = stdout) public System.String Name { get;} public System.String Text { get;} Microsoft.DotNet.Interactive.Documents.Jupyter @@ -75,15 +75,8 @@ Microsoft.DotNet.Interactive.Documents.Jupyter .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#) public static class Notebook - public static System.Text.Encoding Encoding { get;} - public static System.Text.Json.JsonSerializerOptions JsonSerializerOptions { get;} public static Microsoft.DotNet.Interactive.Documents.InteractiveDocument Parse(System.String json, Microsoft.DotNet.Interactive.Documents.KernelInfoCollection kernelInfos = null) public static Microsoft.DotNet.Interactive.Documents.InteractiveDocument Read(System.IO.Stream stream, Microsoft.DotNet.Interactive.Documents.KernelInfoCollection kernelInfos) public static System.String ToJupyterJson(System.String defaultLanguage = null) - public static System.Void Write(Microsoft.DotNet.Interactive.Documents.InteractiveDocument document, System.IO.Stream stream) public static System.Void Write(Microsoft.DotNet.Interactive.Documents.InteractiveDocument document, System.IO.Stream stream, Microsoft.DotNet.Interactive.Documents.KernelInfoCollection kernelInfos) - public static System.Void Write(Microsoft.DotNet.Interactive.Documents.InteractiveDocument document, System.IO.TextWriter writer) - public static System.Void Write(Microsoft.DotNet.Interactive.Documents.InteractiveDocument document, System.IO.TextWriter writer, Microsoft.DotNet.Interactive.Documents.KernelInfoCollection kernelInfos) diff --git a/src/Microsoft.DotNet.Interactive.Documents.Tests/JupyterFormatTests.cs b/src/Microsoft.DotNet.Interactive.Documents.Tests/JupyterFormatTests.cs index ffa54378b2..2c4b01a836 100644 --- a/src/Microsoft.DotNet.Interactive.Documents.Tests/JupyterFormatTests.cs +++ b/src/Microsoft.DotNet.Interactive.Documents.Tests/JupyterFormatTests.cs @@ -88,7 +88,7 @@ public void notebook_metadata_default_language_is_honored_in_cells_without_langu [InlineData("PowerShell", "powershell")] public void Metadata_default_kernel_name_is_based_on_specified_language(string languageName, string kernelName) { - var document = new InteractiveDocument().WithJupyterMetadata(languageName); + var document = Notebook.Parse(new InteractiveDocument().ToJupyterJson(languageName)); document.GetDefaultKernelName() .Should() @@ -102,7 +102,7 @@ public void Metadata_default_kernel_name_is_based_on_specified_language(string l [InlineData("PowerShell", "powershell")] public void Metadata_default_kernel_name_is_based_on_specified_language_when_serialized_and_deserialized(string languageName, string kernelName) { - var originalDoc = new InteractiveDocument().WithJupyterMetadata(languageName); + var originalDoc = Notebook.Parse(new InteractiveDocument().ToJupyterJson(languageName)); var parsedDoc = Notebook.Parse(originalDoc.ToJupyterJson()); diff --git a/src/Microsoft.DotNet.Interactive.Documents/CodeSubmission.cs b/src/Microsoft.DotNet.Interactive.Documents/CodeSubmission.cs index f26651a30b..5a8e08b280 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/CodeSubmission.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/CodeSubmission.cs @@ -53,7 +53,7 @@ public static InteractiveDocument Parse( metadata = JsonSerializer.Deserialize>(metadataString, InteractiveDocument.JsonSerializerOptions); - if (InteractiveDocument.TryGetKernelInfoFromMetadata(metadata, out var kernelInfoFromMetadata)) + if (InteractiveDocument.TryGetKernelInfosFromMetadata(metadata, out var kernelInfoFromMetadata)) { InteractiveDocument.MergeKernelInfos(kernelInfos, kernelInfoFromMetadata); document.Metadata["kernelInfo"] = kernelInfoFromMetadata; diff --git a/src/Microsoft.DotNet.Interactive.Documents/InteractiveDocument.cs b/src/Microsoft.DotNet.Interactive.Documents/InteractiveDocument.cs index 68e60d3637..4498dc1de3 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/InteractiveDocument.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/InteractiveDocument.cs @@ -65,7 +65,7 @@ public async IAsyncEnumerable GetImportsAsync(bool recursiv { EnsureImportFieldParserIsInitialized(); - if (!TryGetKernelInfoFromMetadata(Metadata, out var kernelInfos)) + if (!TryGetKernelInfosFromMetadata(out var kernelInfos)) { kernelInfos = new(); } @@ -198,7 +198,7 @@ public static async Task LoadAsync( public string? GetDefaultKernelName() { - if (TryGetKernelInfoFromMetadata(Metadata, out var kernelInfo)) + if (TryGetKernelInfosFromMetadata(Metadata, out var kernelInfo)) { return kernelInfo.DefaultKernelName; } @@ -208,16 +208,11 @@ public static async Task LoadAsync( internal string? GetDefaultKernelName(KernelInfoCollection kernelInfos) { - if (TryGetKernelInfoFromMetadata(Metadata, out var kernelInfoCollection)) + if (TryGetKernelInfosFromMetadata(Metadata, out var kernelInfoCollection)) { return kernelInfoCollection.DefaultKernelName; } - if (Metadata is null) - { - return null; - } - if (Metadata.TryGetValue("kernelspec", out var kernelspecObj)) { if (kernelspecObj is IDictionary kernelspecDict) @@ -243,7 +238,7 @@ public static async Task LoadAsync( internal static void MergeKernelInfos(InteractiveDocument document, KernelInfoCollection kernelInfos) { - if (TryGetKernelInfoFromMetadata(document.Metadata, out var kernelInfoCollection)) + if (TryGetKernelInfosFromMetadata(document.Metadata, out var kernelInfoCollection)) { MergeKernelInfos(kernelInfoCollection, kernelInfos); } @@ -264,19 +259,23 @@ internal static void MergeKernelInfos(KernelInfoCollection destination, KernelIn destination.AddRange(source.Where(ki => added.Add(ki.Name))); } - internal static bool TryGetKernelInfoFromMetadata( + public bool TryGetKernelInfosFromMetadata( + [NotNullWhen(true)] out KernelInfoCollection? kernelInfos) => + TryGetKernelInfosFromMetadata(Metadata, out kernelInfos); + + internal static bool TryGetKernelInfosFromMetadata( IDictionary? metadata, - [NotNullWhen(true)] out KernelInfoCollection? kernelInfo) + [NotNullWhen(true)] out KernelInfoCollection? kernelInfos) { if (metadata is not null) { if (metadata.TryGetValue("kernelInfo", out var kernelInfoObj)) { if (kernelInfoObj is JsonElement kernelInfoJson && - JsonSerializer.Deserialize(kernelInfoJson, JsonSerializerOptions) is + kernelInfoJson.Deserialize(JsonSerializerOptions) is { } kernelInfoDeserialized) { - kernelInfo = kernelInfoDeserialized; + kernelInfos = kernelInfoDeserialized; return true; } @@ -319,7 +318,7 @@ internal static bool TryGetKernelInfoFromMetadata( deserializedKernelInfo.Add(new KernelInfo(name, language, aliases)); } } - kernelInfo = deserializedKernelInfo; + kernelInfos = deserializedKernelInfo; return true; } } @@ -327,7 +326,7 @@ internal static bool TryGetKernelInfoFromMetadata( if (kernelInfoObj is KernelInfoCollection kernelInfoCollection) { - kernelInfo = kernelInfoCollection; + kernelInfos = kernelInfoCollection; return true; } } @@ -338,17 +337,17 @@ internal static bool TryGetKernelInfoFromMetadata( { case KernelInfoCollection kernelInfoCollection: - kernelInfo = kernelInfoCollection; + kernelInfos = kernelInfoCollection; return true; case IDictionary dotnetInteractiveDict: { - kernelInfo = new(); + kernelInfos = new(); if (dotnetInteractiveDict.TryGetValue("defaultKernelName", out var nameObj) && nameObj is string name) { - kernelInfo.DefaultKernelName = name; + kernelInfos.DefaultKernelName = name; } return true; @@ -364,7 +363,7 @@ internal static bool TryGetKernelInfoFromMetadata( if (kernelspecDict.TryGetValue("language", out var languageObj) && languageObj is string defaultLanguage) { - kernelInfo = new KernelInfoCollection + kernelInfos = new KernelInfoCollection { DefaultKernelName = defaultLanguage }; @@ -375,7 +374,7 @@ internal static bool TryGetKernelInfoFromMetadata( } // check if a KernelInfoCollection was directly serialized into the metadata - kernelInfo = default; + kernelInfos = default; return false; } diff --git a/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InputCellMetadata.cs b/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InputCellMetadata.cs index 4a595a7161..71ca809e4f 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InputCellMetadata.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InputCellMetadata.cs @@ -7,12 +7,15 @@ namespace Microsoft.DotNet.Interactive.Documents.Jupyter; public class InputCellMetadata { - public InputCellMetadata(string? kernelName = null, string ? language = null) + public InputCellMetadata(string? kernelName = null, string? language = null) { KernelName = kernelName; Language = language; } - [JsonPropertyName("kernelName")] public string? KernelName { get; } - [JsonPropertyName("language")] public string? Language { get; } + [JsonPropertyName("kernelName")] + public string? KernelName { get; } + + [JsonPropertyName("language")] + public string? Language { get; } } diff --git a/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InteractiveDocumentExtensions.cs b/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InteractiveDocumentExtensions.cs index 6f8ed91d35..abcbcf8a35 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InteractiveDocumentExtensions.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InteractiveDocumentExtensions.cs @@ -7,7 +7,7 @@ namespace Microsoft.DotNet.Interactive.Documents.Jupyter; -public static class InteractiveDocumentExtensions +internal static class InteractiveDocumentExtensions { public static InteractiveDocument WithJupyterMetadata( this InteractiveDocument document, diff --git a/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InteractiveDocumentOutputElementConverter.cs b/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InteractiveDocumentOutputElementConverter.cs index a87d58f062..7c7419ed09 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InteractiveDocumentOutputElementConverter.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/Jupyter/InteractiveDocumentOutputElementConverter.cs @@ -97,11 +97,16 @@ public override InteractiveDocumentOutputElement Read( break; case "execute_result": - var returnValueElement = new ReturnValueElement(data ?? new Dictionary()) + var returnValueElement = new ReturnValueElement { ExecutionOrder = executionCount ?? 0 }; - + + if (data is not null) + { + returnValueElement.Data.MergeWith(data); + } + if (metadata is not null) { returnValueElement.Metadata.MergeWith(metadata); diff --git a/src/Microsoft.DotNet.Interactive.Documents/Jupyter/Notebook.cs b/src/Microsoft.DotNet.Interactive.Documents/Jupyter/Notebook.cs index 13ad5124d3..c5c953698c 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/Jupyter/Notebook.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/Jupyter/Notebook.cs @@ -13,6 +13,8 @@ namespace Microsoft.DotNet.Interactive.Documents.Jupyter; public static class Notebook { + private static readonly Encoding _encoding = new UTF8Encoding(false); + static Notebook() { JsonSerializerOptions = new JsonSerializerOptions @@ -33,12 +35,7 @@ static Notebook() }; } - public static JsonSerializerOptions JsonSerializerOptions { get; } - - public const string MetadataNamespace = "dotnet_interactive"; - public const string PolyglotMetadataNamespace = "polyglot_notebook"; - - public static Encoding Encoding => new UTF8Encoding(false); + internal static JsonSerializerOptions JsonSerializerOptions { get; } public static InteractiveDocument Parse( string json, @@ -59,24 +56,11 @@ public static InteractiveDocument Read( Stream stream, KernelInfoCollection kernelInfos) { - using var reader = new StreamReader(stream, Encoding); + using var reader = new StreamReader(stream, _encoding); var content = reader.ReadToEnd(); return Parse(content, kernelInfos); } - public static void Write(InteractiveDocument document, Stream stream) - { - using var writer = new StreamWriter(stream, Encoding, 1024, true); - Write(document, writer); - writer.Flush(); - } - - public static void Write(InteractiveDocument document, Stream stream, KernelInfoCollection kernelInfos) - { - InteractiveDocument.MergeKernelInfos(document, kernelInfos); - Write(document, stream); - } - public static string ToJupyterJson( this InteractiveDocument document, string? defaultLanguage = null) @@ -97,15 +81,12 @@ public static string ToJupyterJson( return singleSpaceIndentedJson; } - public static void Write(InteractiveDocument document, TextWriter writer) + public static void Write(InteractiveDocument document, Stream stream, KernelInfoCollection kernelInfos) { + InteractiveDocument.MergeKernelInfos(document, kernelInfos); + using var writer = new StreamWriter(stream, _encoding, 1024, true); var content = document.ToJupyterJson(); writer.Write(content); + writer.Flush(); } - public static void Write(InteractiveDocument document, TextWriter writer, KernelInfoCollection kernelInfos) - { - InteractiveDocument.MergeKernelInfos(document, kernelInfos); - Write(document, writer); - } - } \ No newline at end of file diff --git a/src/Microsoft.DotNet.Interactive.Documents/KernelInfoCollection.cs b/src/Microsoft.DotNet.Interactive.Documents/KernelInfoCollection.cs index 212e1dafd3..04059de5b4 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/KernelInfoCollection.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/KernelInfoCollection.cs @@ -99,7 +99,7 @@ public KernelInfoCollection Clone() return clone; } - public bool TryGetByAlias(string alias, out KernelInfo info) + internal bool TryGetByAlias(string alias, out KernelInfo info) { return _kernelInfoByNameOrAlias!.TryGetValue(alias, out info); } diff --git a/src/Microsoft.DotNet.Interactive.Documents/ReturnValueElement.cs b/src/Microsoft.DotNet.Interactive.Documents/ReturnValueElement.cs index 62739f1fb3..acfb5cf66a 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/ReturnValueElement.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/ReturnValueElement.cs @@ -1,20 +1,14 @@ // 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. -using System; using System.Collections.Generic; namespace Microsoft.DotNet.Interactive.Documents; public class ReturnValueElement : InteractiveDocumentOutputElement, IDataElement { - public ReturnValueElement(IDictionary? data) - { - Data = data ?? throw new ArgumentNullException(nameof(data)); - } + public IDictionary Data { get; } = new Dictionary(); - public IDictionary Data { get; } - public int ExecutionOrder { get; set; } public IDictionary Metadata { get; } = new Dictionary(); diff --git a/src/Microsoft.DotNet.Interactive.Documents/TextElement.cs b/src/Microsoft.DotNet.Interactive.Documents/TextElement.cs index 2c1ae5aaee..625e661aec 100644 --- a/src/Microsoft.DotNet.Interactive.Documents/TextElement.cs +++ b/src/Microsoft.DotNet.Interactive.Documents/TextElement.cs @@ -5,7 +5,7 @@ namespace Microsoft.DotNet.Interactive.Documents; public class TextElement : InteractiveDocumentOutputElement { - public TextElement(string? text, string? name) + public TextElement(string? text, string? name = "stdout") { Text = text ?? ""; Name = name ?? "stdout"; diff --git a/src/Microsoft.DotNet.Interactive.Jupyter/JupyterRequestContextExtensions.cs b/src/Microsoft.DotNet.Interactive.Jupyter/JupyterRequestContextExtensions.cs index a46dd41fcd..06bcb0bdf7 100644 --- a/src/Microsoft.DotNet.Interactive.Jupyter/JupyterRequestContextExtensions.cs +++ b/src/Microsoft.DotNet.Interactive.Jupyter/JupyterRequestContextExtensions.cs @@ -10,13 +10,17 @@ public static class JupyterRequestContextExtensions public static string GetKernelName(this JupyterRequestContext context) { string kernelName = null; - if (context.JupyterRequestMessageEnvelope.MetaData.TryGetValue(Notebook.MetadataNamespace, out var candidateDotnetMetadata) && + if (context.JupyterRequestMessageEnvelope.MetaData.TryGetValue( + "dotnet_interactive", + out var candidateDotnetMetadata) && candidateDotnetMetadata is InputCellMetadata dotnetMetadata) { kernelName = dotnetMetadata.Language; } - if (context.JupyterRequestMessageEnvelope.MetaData.TryGetValue(Notebook.PolyglotMetadataNamespace, out var candidatePolyglotMetadata) && + if (context.JupyterRequestMessageEnvelope.MetaData.TryGetValue( + "polyglot_notebook", + out var candidatePolyglotMetadata) && candidatePolyglotMetadata is InputCellMetadata polyglotMetadata) { kernelName = polyglotMetadata.KernelName;