diff --git a/build/ci.yml b/build/ci.yml
index 76343709d3..3bb8115793 100644
--- a/build/ci.yml
+++ b/build/ci.yml
@@ -4,7 +4,7 @@ trigger:
variables:
Build.Major: 0
- Build.Minor: 10
+ Build.Minor: 11
Drops.Dir: $(Build.ArtifactStagingDirectory)/drops
jobs:
diff --git a/src/Jupyter/CustomShell/ClientInfoHandler.cs b/src/Jupyter/CustomShell/ClientInfoHandler.cs
index bc0b066155..211d0380de 100644
--- a/src/Jupyter/CustomShell/ClientInfoHandler.cs
+++ b/src/Jupyter/CustomShell/ClientInfoHandler.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Jupyter.Core;
using Microsoft.Jupyter.Core.Protocol;
@@ -83,9 +84,11 @@ IShellServer shellServer
this.shellServer = shellServer;
}
+ ///
public string MessageType => "iqsharp_clientinfo_request";
- public void Handle(Message message)
+ ///
+ public async Task HandleAsync(Message message)
{
var content = message.To();
metadata.UserAgent = content.UserAgent ?? metadata.UserAgent;
diff --git a/src/Jupyter/CustomShell/EchoHandler.cs b/src/Jupyter/CustomShell/EchoHandler.cs
index 352b1e4545..29689fa4cb 100644
--- a/src/Jupyter/CustomShell/EchoHandler.cs
+++ b/src/Jupyter/CustomShell/EchoHandler.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Jupyter.Core;
using Microsoft.Jupyter.Core.Protocol;
@@ -34,7 +35,7 @@ IShellServer shellServer
public string MessageType => "iqsharp_echo_request";
- public void Handle(Message message)
+ public async Task HandleAsync(Message message)
{
// Find out the thing we need to echo back.
var value = (message.Content as UnknownContent).Data["value"] as string;
diff --git a/src/Jupyter/Extensions.cs b/src/Jupyter/Extensions.cs
index 624b4935f6..a80b941c41 100644
--- a/src/Jupyter/Extensions.cs
+++ b/src/Jupyter/Extensions.cs
@@ -4,7 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
-using System.Text;
+using System.Text.RegularExpressions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Jupyter.Core;
using Microsoft.Jupyter.Core.Protocol;
@@ -169,5 +169,32 @@ public static T WithStackTraceDisplay(this T simulator, IChannel channel)
};
return simulator;
}
+
+ ///
+ /// Removes common indents from each line in a string,
+ /// similarly to Python's textwrap.dedent() function.
+ ///
+ internal static string Dedent(this string text)
+ {
+ // First, start by finding the length of common indents,
+ // disregarding lines that are only whitespace.
+ var leadingWhitespaceRegex = new Regex(@"^[ \t]*");
+ var minWhitespace = int.MaxValue;
+ foreach (var line in text.Split("\n"))
+ {
+ if (!string.IsNullOrWhiteSpace(line))
+ {
+ var match = leadingWhitespaceRegex.Match(line);
+ minWhitespace = match.Success
+ ? System.Math.Min(minWhitespace, match.Value.Length)
+ : minWhitespace = 0;
+ }
+ }
+
+ // We can use that to build a new regex that strips
+ // out common indenting.
+ var leftTrimRegex = new Regex(@$"^[ \t]{{{minWhitespace}}}", RegexOptions.Multiline);
+ return leftTrimRegex.Replace(text, "");
+ }
}
}
diff --git a/src/Jupyter/IQSharpEngine.cs b/src/Jupyter/IQSharpEngine.cs
index 758a9bb1de..a275044455 100644
--- a/src/Jupyter/IQSharpEngine.cs
+++ b/src/Jupyter/IQSharpEngine.cs
@@ -15,6 +15,7 @@
using Newtonsoft.Json.Converters;
using Microsoft.Jupyter.Core.Protocol;
using Newtonsoft.Json;
+using System.Threading.Tasks;
namespace Microsoft.Quantum.IQSharp.Jupyter
{
@@ -39,7 +40,7 @@ public IQSharpEngine(
PerformanceMonitor performanceMonitor,
IShellRouter shellRouter,
IEventService eventService
- ) : base(shell, context, logger)
+ ) : base(shell, shellRouter, context, logger, services)
{
this.performanceMonitor = performanceMonitor;
performanceMonitor.Start();
@@ -86,7 +87,7 @@ IEventService eventService
/// cell is expected to have a Q# snippet, which gets compiled and we return the name of
/// the operations found. These operations are then available for simulation and estimate.
///
- public override ExecutionResult ExecuteMundane(string input, IChannel channel)
+ public override async Task ExecuteMundane(string input, IChannel channel)
{
channel = channel.WithNewLines();
diff --git a/src/Jupyter/Jupyter.csproj b/src/Jupyter/Jupyter.csproj
index f5d21b940d..4fd43c8606 100644
--- a/src/Jupyter/Jupyter.csproj
+++ b/src/Jupyter/Jupyter.csproj
@@ -22,7 +22,7 @@
-
+
diff --git a/src/Jupyter/Magic/AbstractMagic.cs b/src/Jupyter/Magic/AbstractMagic.cs
index 999d8b4824..f3118d61f3 100644
--- a/src/Jupyter/Magic/AbstractMagic.cs
+++ b/src/Jupyter/Magic/AbstractMagic.cs
@@ -3,7 +3,7 @@
using System;
using System.Collections.Generic;
-
+using System.Threading.Tasks;
using Microsoft.Jupyter.Core;
using Microsoft.Quantum.IQSharp.Common;
using Microsoft.Quantum.IQSharp.Jupyter;
@@ -34,8 +34,8 @@ public AbstractMagic(string keyword, Documentation docs)
/// returned execution function displays the given exceptions to its
/// display channel.
///
- public Func SafeExecute(Func magic) =>
- (input, channel) =>
+ public Func> SafeExecute(Func magic) =>
+ async (input, channel) =>
{
channel = channel.WithNewLines();
diff --git a/src/Jupyter/Magic/ConfigMagic.cs b/src/Jupyter/Magic/ConfigMagic.cs
index 7dd95e7337..7af2e713ec 100644
--- a/src/Jupyter/Magic/ConfigMagic.cs
+++ b/src/Jupyter/Magic/ConfigMagic.cs
@@ -25,7 +25,48 @@ public class ConfigMagic : AbstractMagic
public ConfigMagic(IConfigurationSource configurationSource) : base(
"config",
new Documentation {
- Summary = "Allows setting or querying configuration options."
+ Summary = "Allows setting or querying configuration options.",
+ Description = @"
+ This magic command allows for setting or querying
+ configuration options used to control the behavior of the
+ IQ# kernel (e.g.: state visualization options), and to
+ save those options to a JSON file in the current working
+ directory.
+ ".Dedent(),
+ Examples = new []
+ {
+ @"
+ Print a list of all currently set configuration options:
+ ```
+ In []: %config
+ Out[]: Configuration key Value
+ --------------------------------- -----------
+ dump.basisStateLabelingConvention ""BigEndian""
+ dump.truncateSmallAmplitudes true
+ ```
+ ",
+
+ @"
+ Configure the `DumpMachine` and `DumpRegister` callables
+ to use big-endian convention:
+ ```
+ In []: %config dump.basisStateLabelingConvention = ""BigEndian""
+ Out[]: ""BigEndian""
+ ```
+ ".Dedent(),
+
+ @"
+ Save current configuration options to `.iqsharp-config.json`
+ in the current working directory:
+ ```
+ In []: %config --save
+ Out[]:
+ ```
+ Note that options saved this way will be applied automatically
+ the next time a notebook in the current working
+ directory is loaded.
+ ".Dedent()
+ }
})
{
this.ConfigurationSource = configurationSource;
diff --git a/src/Tests/IQsharpEngineTests.cs b/src/Tests/IQsharpEngineTests.cs
index 84f50ee3cc..10c5e03fd7 100644
--- a/src/Tests/IQsharpEngineTests.cs
+++ b/src/Tests/IQsharpEngineTests.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq;
+using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.Jupyter.Core;
@@ -40,10 +41,10 @@ public static void PrintResult(ExecutionResult result, MockChannel channel)
foreach (var m in channel.msgs) Console.WriteLine($" {m}");
}
- public static string AssertCompile(IQSharpEngine engine, string source, params string[] expectedOps)
+ public static async Task AssertCompile(IQSharpEngine engine, string source, params string[] expectedOps)
{
var channel = new MockChannel();
- var response = engine.ExecuteMundane(source, channel);
+ var response = await engine.ExecuteMundane(source, channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
Assert.AreEqual(0, channel.msgs.Count);
@@ -52,12 +53,12 @@ public static string AssertCompile(IQSharpEngine engine, string source, params s
return response.Output?.ToString();
}
- public static string AssertSimulate(IQSharpEngine engine, string snippetName, params string[] messages)
+ public static async Task AssertSimulate(IQSharpEngine engine, string snippetName, params string[] messages)
{
var configSource = new ConfigurationSource(skipLoading: true);
var simMagic = new SimulateMagic(engine.SymbolsResolver, configSource);
var channel = new MockChannel();
- var response = simMagic.Execute(snippetName, channel);
+ var response = await simMagic.Execute(snippetName, channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
CollectionAssert.AreEqual(messages.Select(ChannelWithNewLines.Format).ToArray(), channel.msgs.ToArray());
@@ -65,11 +66,11 @@ public static string AssertSimulate(IQSharpEngine engine, string snippetName, pa
return response.Output?.ToString();
}
- public static string AssertEstimate(IQSharpEngine engine, string snippetName, params string[] messages)
+ public static async Task AssertEstimate(IQSharpEngine engine, string snippetName, params string[] messages)
{
var channel = new MockChannel();
var estimateMagic = new EstimateMagic(engine.SymbolsResolver);
- var response = estimateMagic.Execute(snippetName, channel);
+ var response = await estimateMagic.Execute(snippetName, channel);
var result = response.Output as DataTable;
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
@@ -84,15 +85,15 @@ public static string AssertEstimate(IQSharpEngine engine, string snippetName, pa
}
[TestMethod]
- public void CompileOne()
+ public async Task CompileOne()
{
var engine = Init();
- AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
+ await AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
}
[TestMethod]
- public void CompileAndSimulate()
+ public async Task CompileAndSimulate()
{
var engine = Init();
var configSource = new ConfigurationSource(skipLoading: true);
@@ -100,7 +101,7 @@ public void CompileAndSimulate()
var channel = new MockChannel();
// Try running without compiling it, fails:
- var response = simMagic.Execute("_snippet_.HelloQ", channel);
+ var response = await simMagic.Execute("_snippet_.HelloQ", channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Error, response.Status);
Assert.AreEqual(0, channel.msgs.Count);
@@ -108,51 +109,51 @@ public void CompileAndSimulate()
Assert.AreEqual(ChannelWithNewLines.Format($"Invalid operation name: _snippet_.HelloQ"), channel.errors[0]);
// Compile it:
- AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
+ await AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
// Try running again:
- AssertSimulate(engine, "HelloQ", "Hello from quantum world!");
+ await AssertSimulate(engine, "HelloQ", "Hello from quantum world!");
}
[TestMethod]
- public void SimulateWithArguments()
+ public async Task SimulateWithArguments()
{
var engine = Init();
// Compile it:
- AssertCompile(engine, SNIPPETS.Reverse, "Reverse");
+ await AssertCompile(engine, SNIPPETS.Reverse, "Reverse");
// Try running again:
- var results = AssertSimulate(engine, "Reverse { \"array\": [2, 3, 4], \"name\": \"foo\" }", "Hello foo");
+ var results = await AssertSimulate(engine, "Reverse { \"array\": [2, 3, 4], \"name\": \"foo\" }", "Hello foo");
Assert.AreEqual("[4,3,2]", results);
}
[TestMethod]
- public void Estimate()
+ public async Task Estimate()
{
var engine = Init();
var channel = new MockChannel();
// Compile it:
- AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
+ await AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
// Try running again:
- AssertEstimate(engine, "HelloQ");
+ await AssertEstimate(engine, "HelloQ");
}
[TestMethod]
- public void Toffoli()
+ public async Task Toffoli()
{
var engine = Init();
var channel = new MockChannel();
// Compile it:
- AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
+ await AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
// Run with toffoli simulator:
var toffoliMagic = new ToffoliMagic(engine.SymbolsResolver);
- var response = toffoliMagic.Execute("HelloQ", channel);
+ var response = await toffoliMagic.Execute("HelloQ", channel);
var result = response.Output as Dictionary;
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
@@ -161,62 +162,62 @@ public void Toffoli()
}
[TestMethod]
- public void DependsOnWorkspace()
+ public async Task DependsOnWorkspace()
{
var engine = Init();
// Compile it:
- AssertCompile(engine, SNIPPETS.DependsOnWorkspace, "DependsOnWorkspace");
+ await AssertCompile(engine, SNIPPETS.DependsOnWorkspace, "DependsOnWorkspace");
// Run:
- var results = AssertSimulate(engine, "DependsOnWorkspace", "Hello Foo again!");
+ var results = await AssertSimulate(engine, "DependsOnWorkspace", "Hello Foo again!");
Assert.AreEqual("[Zero,One,Zero,Zero,Zero]", results);
}
[TestMethod]
- public void UpdateSnippet()
+ public async Task UpdateSnippet()
{
var engine = Init();
// Compile it:
- AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
+ await AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
// Run:
- AssertSimulate(engine, "HelloQ", "Hello from quantum world!");
+ await AssertSimulate(engine, "HelloQ", "Hello from quantum world!");
// Compile it with a new code
- AssertCompile(engine, SNIPPETS.HelloQ_2, "HelloQ");
+ await AssertCompile(engine, SNIPPETS.HelloQ_2, "HelloQ");
// Run again:
- AssertSimulate(engine, "HelloQ", "msg0", "msg1");
+ await AssertSimulate(engine, "HelloQ", "msg0", "msg1");
}
[TestMethod]
- public void UpdateDependency()
+ public async Task UpdateDependency()
{
var engine = Init();
// Compile HelloQ
- AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
+ await AssertCompile(engine, SNIPPETS.HelloQ, "HelloQ");
// Compile something that depends on it:
- AssertCompile(engine, SNIPPETS.DependsOnHelloQ, "DependsOnHelloQ");
+ await AssertCompile(engine, SNIPPETS.DependsOnHelloQ, "DependsOnHelloQ");
// Compile new version of HelloQ
- AssertCompile(engine, SNIPPETS.HelloQ_2, "HelloQ");
+ await AssertCompile(engine, SNIPPETS.HelloQ_2, "HelloQ");
// Run dependency, it should reflect changes on HelloQ:
- AssertSimulate(engine, "DependsOnHelloQ", "msg0", "msg1");
+ await AssertSimulate(engine, "DependsOnHelloQ", "msg0", "msg1");
}
[TestMethod]
- public void ReportWarnings()
+ public async Task ReportWarnings()
{
var engine = Init();
{
var channel = new MockChannel();
- var response = engine.ExecuteMundane(SNIPPETS.ThreeWarnings, channel);
+ var response = await engine.ExecuteMundane(SNIPPETS.ThreeWarnings, channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
Assert.AreEqual(3, channel.msgs.Count);
@@ -226,7 +227,7 @@ public void ReportWarnings()
{
var channel = new MockChannel();
- var response = engine.ExecuteMundane(SNIPPETS.OneWarning, channel);
+ var response = await engine.ExecuteMundane(SNIPPETS.OneWarning, channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
Assert.AreEqual(1, channel.msgs.Count);
@@ -236,12 +237,12 @@ public void ReportWarnings()
}
[TestMethod]
- public void ReportErrors()
+ public async Task ReportErrors()
{
var engine = Init();
var channel = new MockChannel();
- var response = engine.ExecuteMundane(SNIPPETS.TwoErrors, channel);
+ var response = await engine.ExecuteMundane(SNIPPETS.TwoErrors, channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Error, response.Status);
Assert.AreEqual(0, channel.msgs.Count);
@@ -249,13 +250,13 @@ public void ReportErrors()
}
[TestMethod]
- public void TestPackages()
+ public async Task TestPackages()
{
var engine = Init();
var snippets = engine.Snippets as Snippets;
var pkgMagic = new PackageMagic(snippets.GlobalReferences);
var channel = new MockChannel();
- var response = pkgMagic.Execute("", channel);
+ var response = await pkgMagic.Execute("", channel);
var result = response.Output as string[];
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
@@ -265,11 +266,11 @@ public void TestPackages()
// Try compiling TrotterEstimateEnergy, it should fail due to the lack
// of chemistry package.
- response = engine.ExecuteMundane(SNIPPETS.TrotterEstimateEnergy, channel);
+ response = await engine.ExecuteMundane(SNIPPETS.TrotterEstimateEnergy, channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Error, response.Status);
- response = pkgMagic.Execute("microsoft.quantum.chemistry", channel);
+ response = await pkgMagic.Execute("microsoft.quantum.chemistry", channel);
result = response.Output as string[];
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
@@ -278,19 +279,19 @@ public void TestPackages()
Assert.AreEqual(2, result.Length);
// Now it should compile:
- AssertCompile(engine, SNIPPETS.TrotterEstimateEnergy, "TrotterEstimateEnergy");
+ await AssertCompile(engine, SNIPPETS.TrotterEstimateEnergy, "TrotterEstimateEnergy");
}
[TestMethod]
- public void TestInvalidPackages()
+ public async Task TestInvalidPackages()
{
var engine = Init();
var snippets = engine.Snippets as Snippets;
var pkgMagic = new PackageMagic(snippets.GlobalReferences);
var channel = new MockChannel();
- var response = pkgMagic.Execute("microsoft.quantum", channel);
+ var response = await pkgMagic.Execute("microsoft.quantum", channel);
var result = response.Output as string[];
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Error, response.Status);
@@ -301,7 +302,7 @@ public void TestInvalidPackages()
[TestMethod]
- public void TestWho()
+ public async Task TestWho()
{
var snippets = Startup.Create("Workspace");
snippets.Compile(SNIPPETS.HelloQ);
@@ -310,7 +311,7 @@ public void TestWho()
var channel = new MockChannel();
// Check the workspace, it should be in error state:
- var response = whoMagic.Execute("", channel);
+ var response = await whoMagic.Execute("", channel);
var result = response.Output as string[];
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
@@ -320,7 +321,7 @@ public void TestWho()
}
[TestMethod]
- public void TestWorkspace()
+ public async Task TestWorkspace()
{
var engine = Init("Workspace.Chemistry");
var snippets = engine.Snippets as Snippets;
@@ -332,49 +333,49 @@ public void TestWorkspace()
var result = new string[0];
// Check the workspace, it should be in error state:
- var response = wsMagic.Execute("reload", channel);
+ var response = await wsMagic.Execute("reload", channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Error, response.Status);
- response = wsMagic.Execute("", channel);
+ response = await wsMagic.Execute("", channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Error, response.Status);
// Try compiling a snippet that depends on a workspace that depends on the chemistry package:
- response = engine.ExecuteMundane(SNIPPETS.DependsOnChemistryWorkspace, channel);
+ response = await engine.ExecuteMundane(SNIPPETS.DependsOnChemistryWorkspace, channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Error, response.Status);
Assert.AreEqual(0, channel.msgs.Count);
// Add dependencies:
- response = pkgMagic.Execute("microsoft.quantum.chemistry", channel);
+ response = await pkgMagic.Execute("microsoft.quantum.chemistry", channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
- response = pkgMagic.Execute("microsoft.quantum.research", channel);
+ response = await pkgMagic.Execute("microsoft.quantum.research", channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
// Reload workspace:
- response = wsMagic.Execute("reload", channel);
+ response = await wsMagic.Execute("reload", channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
- response = wsMagic.Execute("", channel);
+ response = await wsMagic.Execute("", channel);
result = response.Output as string[];
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
Assert.AreEqual(3, result.Length);
// Now compilation must work:
- AssertCompile(engine, SNIPPETS.DependsOnChemistryWorkspace, "DependsOnChemistryWorkspace");
+ await AssertCompile(engine, SNIPPETS.DependsOnChemistryWorkspace, "DependsOnChemistryWorkspace");
// Check an invalid command
- response = wsMagic.Execute("foo", channel);
+ response = await wsMagic.Execute("foo", channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Error, response.Status);
// Check that everything still works:
- response = wsMagic.Execute("", channel);
+ response = await wsMagic.Execute("", channel);
PrintResult(response, channel);
Assert.AreEqual(ExecuteStatus.Ok, response.Status);
}
diff --git a/src/Tests/Mocks.cs b/src/Tests/Mocks.cs
index 6bbbd26ffd..11fcb4f645 100644
--- a/src/Tests/Mocks.cs
+++ b/src/Tests/Mocks.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using Microsoft.Jupyter.Core;
@@ -71,17 +72,17 @@ public MockShellRouter(MockShell shell)
this.shell = shell;
}
- public void Handle(Message message)
+ public async Task Handle(Message message)
{
shell.Handle(message);
}
- public void RegisterFallback(Action fallback)
+ public void RegisterFallback(Func fallback)
{
throw new NotImplementedException();
}
- public void RegisterHandler(string messageType, Action handler)
+ public void RegisterHandler(string messageType, Func handler)
{
}