Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build/test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ function Test-One {
--no-build `
--logger trx `
--filter $_ `
<# Enable additional output from the test logger, but
without also turning on lots of additional noise from msbuild.
#> `
--logger:"console;verbosity=detailed" `
/property:DefineConstants=$Env:ASSEMBLY_CONSTANTS `
/property:InformationalVersion=$Env:SEMVER_VERSION `
/property:Version=$Env:ASSEMBLY_VERSION
Expand Down
28 changes: 11 additions & 17 deletions src/Core/Loggers/NugetLogger.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#nullable enable

using System;
using System.Collections.Generic;
Expand All @@ -14,31 +15,24 @@ namespace Microsoft.Quantum.IQSharp.Common
/// </summary>
public class NuGetLogger : NuGet.Common.LoggerBase
{
private ILogger _logger { get; set; }
private ILogger? _logger { get; set; }
public List<NuGet.Common.ILogMessage> Logs { get; private set; }

public NuGetLogger(ILogger logger)
public NuGetLogger(ILogger? logger)
{
_logger = logger;
this.Logs = new List<NuGet.Common.ILogMessage>();
}

public static LogLevel MapLevel(NuGet.Common.LogLevel original)
{
switch (original)
public static LogLevel MapLevel(NuGet.Common.LogLevel original) =>
original switch
{
case NuGet.Common.LogLevel.Error:
return LogLevel.Error;
case NuGet.Common.LogLevel.Warning:
return LogLevel.Warning;
case NuGet.Common.LogLevel.Information:
return LogLevel.Information;
case NuGet.Common.LogLevel.Debug:
return LogLevel.Debug;
default:
return LogLevel.Trace;
}
}
NuGet.Common.LogLevel.Error => LogLevel.Error,
NuGet.Common.LogLevel.Warning => LogLevel.Warning,
NuGet.Common.LogLevel.Information => LogLevel.Information,
NuGet.Common.LogLevel.Debug => LogLevel.Debug,
_ => LogLevel.Trace
};

public override void Log(NuGet.Common.ILogMessage m)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Core/References/NugetPackages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public IEnumerable<SourceRepository> Repositories

public NugetPackages(
IOptions<Settings> config,
ILogger<NugetPackages> logger
ILogger<NugetPackages>? logger
)
{
this.Logger = new NuGetLogger(logger);
Expand Down
2 changes: 1 addition & 1 deletion src/Jupyter/Jupyter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Jupyter.Core" Version="1.5.124273" />
<PackageReference Include="Microsoft.Jupyter.Core" Version="1.5.124810" />
<PackageReference Include="System.Reactive" Version="4.3.2" />
</ItemGroup>

Expand Down
20 changes: 10 additions & 10 deletions src/Kernel/CustomShell/ClientInfoHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,34 +17,34 @@ namespace Microsoft.Quantum.IQSharp.Kernel
internal class ClientInfoContent : MessageContent
{
[JsonProperty("hosting_environment")]
public string HostingEnvironment { get; set; }
public string? HostingEnvironment { get; set; }

[JsonProperty("iqsharp_version")]
public string IQSharpVersion { get; set; }
public string? IQSharpVersion { get; set; }

[JsonProperty("user_agent")]
public string UserAgent { get; set; }
public string? UserAgent { get; set; }

[JsonProperty("client_id")]
public string ClientId { get; set; }
public string? ClientId { get; set; }

[JsonProperty("client_isnew")]
public bool? ClientIsNew { get; set; }

[JsonProperty("client_host")]
public string ClientHost { get; set; }
public string? ClientHost { get; set; }

[JsonProperty("client_origin")]
public string ClientOrigin { get; set; }
public string? ClientOrigin { get; set; }

[JsonProperty("client_first_origin")]
public string ClientFirstOrigin { get; set; }
public string? ClientFirstOrigin { get; set; }

[JsonProperty("client_language")]
public string ClientLanguage { get; set; }
public string? ClientLanguage { get; set; }

[JsonProperty("client_country")]
public string ClientCountry { get; set; }
public string? ClientCountry { get; set; }

[JsonProperty("telemetry_opt_out")]
public bool? TelemetryOptOut { get; set; }
Expand All @@ -69,7 +69,7 @@ internal static ClientInfoContent AsClientInfoContent(this IMetadataController m
/// </summary>
internal class ClientInfoHandler : IShellHandler
{
public string UserAgent { get; private set; }
public string? UserAgent { get; private set; }
private readonly ILogger<ClientInfoHandler> logger;
private readonly IMetadataController metadata;
private readonly IShellServer shellServer;
Expand Down
4 changes: 2 additions & 2 deletions src/Kernel/CustomShell/EchoHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Microsoft.Quantum.IQSharp.Kernel
internal class EchoReplyContent : MessageContent
{
[JsonProperty("value")]
public string Value { get; set; }
public string Value { get; set; } = "";
}

/// <summary>
Expand Down Expand Up @@ -44,7 +44,7 @@ IShellServer shellServer
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;
var value = (message.Content as UnknownContent)?.Data?["value"] as string ?? "";
// Send the echo both as an output and as a reply so that clients
// can test both kinds of callbacks.
await Task.Run(() =>
Expand Down
26 changes: 18 additions & 8 deletions src/Kernel/CustomShell/MessageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,28 @@ public static class MessageExtensions
public static T To<T>(this Message message)
where T : MessageContent
{
var content = (message.Content as UnknownContent);
var result = Activator.CreateInstance<T>();
foreach (var property in typeof(T).GetProperties().Where(p => p.CanWrite))
if (message.Content is UnknownContent content)
{
var jsonPropertyAttribute = property.GetCustomAttributes(true).OfType<JsonPropertyAttribute>().FirstOrDefault();
var propertyName = jsonPropertyAttribute?.PropertyName ?? property.Name;
if (content.Data.TryGetValue(propertyName, out var value))
var result = Activator.CreateInstance<T>();
foreach (var property in typeof(T).GetProperties().Where(p => p.CanWrite))
{
property.SetValue(result, content.Data[propertyName]);
var jsonPropertyAttribute = property.GetCustomAttributes(true).OfType<JsonPropertyAttribute>().FirstOrDefault();
var propertyName = jsonPropertyAttribute?.PropertyName ?? property.Name;
if (content.Data.TryGetValue(propertyName, out var value))
{
property.SetValue(result, content.Data[propertyName]);
}
}
return result;
}
else if (message.Content is T decoded)
{
return decoded;
}
else
{
throw new Exception($"Attempted to convert a message with content type {message.Content.GetType()} to content type {typeof(T)}; can only convert from unknown content or subclasses of the desired content type.");
}
return result;
}
}
}
86 changes: 67 additions & 19 deletions src/Kernel/IQSharpEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
using System.Threading.Tasks;
using System.Collections.Immutable;
using Microsoft.Quantum.IQSharp.AzureClient;
using Microsoft.Quantum.QsCompiler.BondSchemas;

namespace Microsoft.Quantum.IQSharp.Kernel
{
Expand All @@ -29,6 +30,24 @@ namespace Microsoft.Quantum.IQSharp.Kernel
public class IQSharpEngine : BaseEngine
{
private readonly PerformanceMonitor performanceMonitor;
private readonly IConfigurationSource configurationSource;
private readonly IServiceProvider services;
private readonly ILogger<IQSharpEngine> logger;

// NB: These properties may be null if the engine has not fully started
// up yet.
internal ISnippets? Snippets { get; private set; } = null;

internal ISymbolResolver? SymbolsResolver { get; private set; } = null;

internal IMagicSymbolResolver? MagicResolver { get; private set; } = null;

internal IWorkspace? Workspace { get; private set; } = null;

private TaskCompletionSource<bool> initializedSource = new TaskCompletionSource<bool>();

/// <inheritdoc />
public override Task Initialized => initializedSource.Task;

/// <summary>
/// The main constructor. It expects an `ISnippets` instance that takes care
Expand All @@ -41,20 +60,42 @@ public IQSharpEngine(
IServiceProvider services,
IConfigurationSource configurationSource,
PerformanceMonitor performanceMonitor,
IShellRouter shellRouter,
IEventService eventService,
IMagicSymbolResolver magicSymbolResolver,
IReferences references
IShellRouter shellRouter
) : base(shell, shellRouter, context, logger, services)
{
this.performanceMonitor = performanceMonitor;
performanceMonitor.Start();
this.configurationSource = configurationSource;
this.services = services;
this.logger = logger;
}

/// <inheritdoc />
public override void Start()
{
base.Start();

// Before anything else, make sure to start the right background
// thread on the Q# compilation loader to initialize serializers
// and deserializers. Since that runs in the background, starting
// the engine should not be blocked, and other services can
// continue to initialize while the Q# compilation loader works.
//
// For more details, see:
// https://github.com/microsoft/qsharp-compiler/pull/727
// https://github.com/microsoft/qsharp-compiler/pull/810
logger.LogDebug("Loading serialization and deserialziation protocols.");
Protocols.Initialize();

logger.LogDebug("Getting services required to start IQ# engine.");
this.Snippets = services.GetService<ISnippets>();
this.SymbolsResolver = services.GetService<ISymbolResolver>();
this.MagicResolver = magicSymbolResolver;
this.MagicResolver = services.GetService<IMagicSymbolResolver>();
this.Workspace = services.GetService<IWorkspace>();
var references = services.GetService<IReferences>();
var eventService = services.GetService<IEventService>();

logger.LogDebug("Registering IQ# display and JSON encoders.");
RegisterDisplayEncoder(new IQSharpSymbolToHtmlResultEncoder());
RegisterDisplayEncoder(new IQSharpSymbolToTextResultEncoder());
RegisterDisplayEncoder(new TaskStatusToTextEncoder());
Expand All @@ -71,13 +112,15 @@ IReferences references
.Concat(AzureClient.JsonConverters.AllConverters)
.ToArray());

logger.LogDebug("Registering IQ# symbol resolvers.");
RegisterSymbolResolver(this.SymbolsResolver);
RegisterSymbolResolver(this.MagicResolver);

logger.LogDebug("Loading known assemblies and registering package loading.");
RegisterPackageLoadedEvent(services, logger, references);

// Handle new shell messages.
shellRouter.RegisterHandlers<IQSharpEngine>();
ShellRouter.RegisterHandlers<IQSharpEngine>();

// Report performance after completing startup.
performanceMonitor.Report();
Expand All @@ -86,8 +129,12 @@ IReferences references
Process.GetCurrentProcess().Id
);


eventService?.TriggerServiceInitialized<IExecutionEngine>(this);

var initializedSuccessfully = initializedSource.TrySetResult(true);
#if DEBUG
Debug.Assert(initializedSuccessfully, "Was unable to complete initialization task.");
#endif
}

/// <summary>
Expand All @@ -108,14 +155,17 @@ private void RegisterPackageLoadedEvent(IServiceProvider services, ILogger logge
// Register new display encoders when packages load.
references.PackageLoaded += (sender, args) =>
{
logger.LogDebug("Scanning for display encoders after loading {Package}.", args.PackageId);
logger.LogDebug("Scanning for display encoders and magic symbols after loading {Package}.", args.PackageId);
foreach (var assembly in references.Assemblies
.Select(asm => asm.Assembly)
.Where(asm => !knownAssemblies.Contains(asm.GetName()))
)
{
// Look for display encoders in the new assembly.
logger.LogDebug("Found new assembly {Name}, looking for display encoders.", assembly.FullName);
logger.LogDebug("Found new assembly {Name}, looking for display encoders and magic symbols.", assembly.FullName);
// Use the magic resolver to find magic symbols in the new assembly;
// it will cache the results for the next magic resolution.
this.MagicResolver?.FindMagic(new AssemblyInfo(assembly));

// If types from an assembly cannot be loaded, log a warning and continue.
var relevantTypes = Enumerable.Empty<Type>();
Expand Down Expand Up @@ -167,14 +217,6 @@ private void RegisterPackageLoadedEvent(IServiceProvider services, ILogger logge
};
}

internal ISnippets Snippets { get; }

internal ISymbolResolver SymbolsResolver { get; }

internal ISymbolResolver MagicResolver { get; }

internal IWorkspace Workspace { get; }

/// <summary>
/// This is the method used to execute Jupyter "normal" cells. In this case, a normal
/// cell is expected to have a Q# snippet, which gets compiled and we return the name of
Expand All @@ -188,9 +230,15 @@ public override async Task<ExecutionResult> ExecuteMundane(string input, IChanne
{
try
{
await Workspace.Initialization;
await this.Initialized;
// Once the engine is initialized, we know that Workspace
// and Snippets are both not-null.
var workspace = this.Workspace!;
var snippets = this.Snippets!;

await workspace.Initialization;

var code = Snippets.Compile(input);
var code = snippets.Compile(input);

foreach (var m in code.warnings) { channel.Stdout(m); }

Expand Down
1 change: 1 addition & 0 deletions src/Kernel/Kernel.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<RootNamespace>Microsoft.Quantum.IQSharp.Kernel</RootNamespace>
<AssemblyName>Microsoft.Quantum.IQSharp.Kernel</AssemblyName>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions src/Kernel/KernelApp/IQSharpKernelApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static class KernelEventsExtensions
/// </summary>
/// <param name="eventService">The event service where the EventSubPub lives</param>
/// <returns>The typed EventPubSub for the KernelStarted event</returns>
public static EventPubSub<KernelStartedEvent, IQSharpKernelApp> OnKernelStarted(this IEventService eventService)
public static EventPubSub<KernelStartedEvent, IQSharpKernelApp>? OnKernelStarted(this IEventService eventService)
{
return eventService?.Events<KernelStartedEvent, IQSharpKernelApp>();
}
Expand All @@ -73,7 +73,7 @@ public static EventPubSub<KernelStartedEvent, IQSharpKernelApp> OnKernelStarted(
/// </summary>
/// <param name="eventService">The event service where the EventSubPub lives</param>
/// <returns>The typed EventPubSub for the KernelStopped event</returns>
public static EventPubSub<KernelStoppedEvent, IQSharpKernelApp> OnKernelStopped(this IEventService eventService)
public static EventPubSub<KernelStoppedEvent, IQSharpKernelApp>? OnKernelStopped(this IEventService eventService)
{
return eventService?.Events<KernelStoppedEvent, IQSharpKernelApp>();
}
Expand Down
Loading