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
Show all changes
61 commits
Select commit Hold shift + click to select a range
89282df
Updates to IQ# syntax highlighting
rmshaffer May 28, 2020
f53eb9c
Validate targets and load provider packages
rmshaffer May 28, 2020
75a8e35
Update Python interface for Azure commands
rmshaffer May 29, 2020
1787ea4
Merge branch 'feature/azure-client' into rmshaffer/azure-targets
rmshaffer Jun 1, 2020
52098e6
Simplify AzureExecutionTarget class
rmshaffer Jun 1, 2020
53fd8e9
Generate EntryPoint and compile into new assembly
rmshaffer Jun 1, 2020
a33a2c9
Changes for JobNotCompleted case
rmshaffer Jun 2, 2020
e47c9df
Merge branch 'rmshaffer/azure-targets' into rmshaffer/azure-entrypoint
rmshaffer Jun 2, 2020
4449040
Refactor entry point code into new classes
rmshaffer Jun 2, 2020
6f3a6fe
Use correct input and output types
rmshaffer Jun 2, 2020
d47fddd
Simplify property syntax
Jun 2, 2020
5215155
Add simple tests for AzureExecutionTarget class
rmshaffer Jun 2, 2020
eeb4b11
Merge branch 'rmshaffer/azure-targets' into rmshaffer/azure-entrypoint
rmshaffer Jun 2, 2020
5e62413
Recompile everything for specified execution target
rmshaffer Jun 3, 2020
26a79ea
Add tests and error handling
rmshaffer Jun 3, 2020
69a4fe6
Improve variable names
rmshaffer Jun 3, 2020
c63a006
Add status message while loading provider package
rmshaffer Jun 3, 2020
1c7d83f
Merge branch 'feature/azure-client' into rmshaffer/azure-targets
rmshaffer Jun 3, 2020
cbdd00e
Merge branch 'rmshaffer/azure-targets' into rmshaffer/azure-entrypoint
rmshaffer Jun 3, 2020
944cec0
Merge branch 'feature/azure-client' into rmshaffer/azure-targets
rmshaffer Jun 3, 2020
12c7e12
Merge branch 'rmshaffer/azure-targets' into rmshaffer/azure-entrypoint
rmshaffer Jun 3, 2020
b8e680b
Merge branch 'feature/azure-client' into rmshaffer/azure-targets
rmshaffer Jun 3, 2020
126f43b
Merge branch 'rmshaffer/azure-targets' into rmshaffer/azure-entrypoint
rmshaffer Jun 3, 2020
43c07ae
Add documentation to AzureExecutionTarget.GetProvider
rmshaffer Jun 5, 2020
2552632
Merge branch 'feature/azure-client' into rmshaffer/azure-targets
rmshaffer Jun 5, 2020
4bf71db
Merge branch 'rmshaffer/azure-targets' into rmshaffer/azure-entrypoint
rmshaffer Jun 5, 2020
52c481d
Extend tests to call EntryPoint.SubmitAsync
rmshaffer Jun 5, 2020
a470662
Wait for job completion on %azure.execute
rmshaffer Jun 5, 2020
354d826
Update comment
rmshaffer Jun 5, 2020
9041701
Add timeout for %azure.execute
rmshaffer Jun 5, 2020
0300ecd
Minor fixes in AzureClient
rmshaffer Jun 5, 2020
8ce9c63
Minor formatting and comment tweaks
rmshaffer Jun 5, 2020
e3e7bcc
Style improvements in test code
rmshaffer Jun 5, 2020
f807723
More consistent handling of job objects
rmshaffer Jun 5, 2020
1409213
Consistent error handling for IWorkspace calls
rmshaffer Jun 5, 2020
78196ff
Update to latest QDK released version
rmshaffer Jun 5, 2020
fce176e
Merge branch 'feature/azure-client' into rmshaffer/azure-entrypoint
rmshaffer Jun 8, 2020
0164134
Merge branch 'feature/azure-client' into rmshaffer/azure-entrypoint
rmshaffer Jun 8, 2020
edbe96e
Add encoders for CloudJob and TargetStatus
rmshaffer Jun 8, 2020
1b27c4b
Move extension methods into encoder files
rmshaffer Jun 8, 2020
3dabab4
Change signature of CloudJob.ToDictionary
rmshaffer Jun 8, 2020
7c69473
Update histogram deserialization and add encoders
rmshaffer Jun 8, 2020
a9bc3d3
Use JsonConverter classes directly
rmshaffer Jun 8, 2020
c9dec08
Small cleanup
rmshaffer Jun 8, 2020
b92db91
Register single JsonEncoder
rmshaffer Jun 8, 2020
bb82ac4
Add a simple HTML histogram display based on StateVectorToHtmlResultE…
rmshaffer Jun 9, 2020
78886f7
Various improvements from PR suggestions
rmshaffer Jun 9, 2020
274ebde
Use UTC for dates returned to Python
rmshaffer Jun 9, 2020
62a358e
Support jobName and shots parameters
rmshaffer Jun 9, 2020
204ed5a
Unify submission and execution code
rmshaffer Jun 9, 2020
7450585
Add some documentation to AzureSubmissionContext
rmshaffer Jun 9, 2020
bc90371
Documentation fix
rmshaffer Jun 9, 2020
f30a387
Add timeout and pollingInterval parameters
rmshaffer Jun 9, 2020
9b3b74b
Use switch syntax for entryPointInput
Jun 10, 2020
eb8de48
Remove 'All rights reserved.'
Jun 10, 2020
0429635
Addressing PR feedback and compiler warnings
rmshaffer Jun 11, 2020
5213b99
Use LINQ for decodedParameters
rmshaffer Jun 11, 2020
7e1146e
Merge branch 'rmshaffer/azure-entrypoint' into rmshaffer/azure-encoders
rmshaffer Jun 11, 2020
c0a7ce0
Merge branch 'rmshaffer/azure-encoders' into rmshaffer/azure-jobsettings
rmshaffer Jun 11, 2020
b75a891
Update parameter names for consistency
rmshaffer Jun 12, 2020
a000b92
Merge branch 'feature/azure-client' into rmshaffer/azure-jobsettings
rmshaffer Jun 12, 2020
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
38 changes: 19 additions & 19 deletions src/AzureClient/AzureClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public async Task<ExecutionResult> GetConnectionStatusAsync(IChannel channel)
return ValidExecutionTargets.ToExecutionResult();
}

private async Task<ExecutionResult> SubmitOrExecuteJobAsync(IChannel channel, string operationName, Dictionary<string, string> inputParameters, bool execute)
private async Task<ExecutionResult> SubmitOrExecuteJobAsync(IChannel channel, AzureSubmissionContext submissionContext, bool execute)
{
if (ActiveWorkspace == null)
{
Expand All @@ -187,7 +187,7 @@ private async Task<ExecutionResult> SubmitOrExecuteJobAsync(IChannel channel, st
return AzureClientError.NoTarget.ToExecutionResult();
}

if (string.IsNullOrEmpty(operationName))
if (string.IsNullOrEmpty(submissionContext.OperationName))
{
var commandName = execute ? "%azure.execute" : "%azure.submit";
channel.Stderr($"Please pass a valid Q# operation name to {commandName}.");
Expand All @@ -202,40 +202,42 @@ private async Task<ExecutionResult> SubmitOrExecuteJobAsync(IChannel channel, st
return AzureClientError.InvalidTarget.ToExecutionResult();
}

channel.Stdout($"Submitting {operationName} to target {ActiveTarget.TargetId}...");
channel.Stdout($"Submitting {submissionContext.OperationName} to target {ActiveTarget.TargetId}...");

IEntryPoint? entryPoint = null;
try
{
entryPoint = EntryPointGenerator.Generate(operationName, ActiveTarget.TargetId);
entryPoint = EntryPointGenerator.Generate(submissionContext.OperationName, ActiveTarget.TargetId);
}
catch (UnsupportedOperationException e)
{
channel.Stderr($"{operationName} is not a recognized Q# operation name.");
channel.Stderr($"{submissionContext.OperationName} is not a recognized Q# operation name.");
return AzureClientError.UnrecognizedOperationName.ToExecutionResult();
}
catch (CompilationErrorsException e)
{
channel.Stderr($"The Q# operation {operationName} could not be compiled as an entry point for job execution.");
channel.Stderr($"The Q# operation {submissionContext.OperationName} could not be compiled as an entry point for job execution.");
foreach (var message in e.Errors) channel.Stderr(message);
return AzureClientError.InvalidEntryPoint.ToExecutionResult();
}

try
{
var job = await entryPoint.SubmitAsync(machine, inputParameters);
channel.Stdout($"Job {job.Id} submitted successfully.");
var job = await entryPoint.SubmitAsync(machine, submissionContext);
channel.Stdout($"Job successfully submitted for {submissionContext.Shots} shots.");
channel.Stdout($" Job name: {submissionContext.FriendlyName}");
channel.Stdout($" Job ID: {job.Id}");
MostRecentJobId = job.Id;
}
catch (ArgumentException e)
{
channel.Stderr($"Failed to parse all expected parameters for Q# operation {operationName}.");
channel.Stderr($"Failed to parse all expected parameters for Q# operation {submissionContext.OperationName}.");
channel.Stderr(e.Message);
return AzureClientError.JobSubmissionFailed.ToExecutionResult();
}
catch (Exception e)
{
channel.Stderr($"Failed to submit Q# operation {operationName} for execution.");
channel.Stderr($"Failed to submit Q# operation {submissionContext.OperationName} for execution.");
channel.Stderr(e.InnerException?.Message ?? e.Message);
return AzureClientError.JobSubmissionFailed.ToExecutionResult();
}
Expand All @@ -245,18 +247,16 @@ private async Task<ExecutionResult> SubmitOrExecuteJobAsync(IChannel channel, st
return await GetJobStatusAsync(channel, MostRecentJobId);
}

var timeoutInSeconds = 30;
channel.Stdout($"Waiting up to {timeoutInSeconds} seconds for Azure Quantum job to complete...");
channel.Stdout($"Waiting up to {submissionContext.ExecutionTimeout} seconds for Azure Quantum job to complete...");

using (var cts = new System.Threading.CancellationTokenSource(TimeSpan.FromSeconds(30)))
using (var cts = new System.Threading.CancellationTokenSource(TimeSpan.FromSeconds(submissionContext.ExecutionTimeout)))
{
CloudJob? cloudJob = null;
do
{
// TODO: Once jupyter-core supports interrupt requests (https://github.com/microsoft/jupyter-core/issues/55),
// handle Jupyter kernel interrupt here and break out of this loop
var pollingIntervalInSeconds = 5;
await Task.Delay(TimeSpan.FromSeconds(pollingIntervalInSeconds));
await Task.Delay(TimeSpan.FromSeconds(submissionContext.ExecutionPollingInterval));
if (cts.IsCancellationRequested) break;
cloudJob = await GetCloudJob(MostRecentJobId);
channel.Stdout($"[{DateTime.Now.ToLongTimeString()}] Current job status: {cloudJob?.Status ?? "Unknown"}");
Expand All @@ -268,12 +268,12 @@ private async Task<ExecutionResult> SubmitOrExecuteJobAsync(IChannel channel, st
}

/// <inheritdoc/>
public async Task<ExecutionResult> SubmitJobAsync(IChannel channel, string operationName, Dictionary<string, string> inputParameters) =>
await SubmitOrExecuteJobAsync(channel, operationName, inputParameters, execute: false);
public async Task<ExecutionResult> SubmitJobAsync(IChannel channel, AzureSubmissionContext submissionContext) =>
await SubmitOrExecuteJobAsync(channel, submissionContext, execute: false);

/// <inheritdoc/>
public async Task<ExecutionResult> ExecuteJobAsync(IChannel channel, string operationName, Dictionary<string, string> inputParameters) =>
await SubmitOrExecuteJobAsync(channel, operationName, inputParameters, execute: true);
public async Task<ExecutionResult> ExecuteJobAsync(IChannel channel, AzureSubmissionContext submissionContext) =>
await SubmitOrExecuteJobAsync(channel, submissionContext, execute: true);

/// <inheritdoc/>
public async Task<ExecutionResult> GetActiveTargetAsync(IChannel channel)
Expand Down
92 changes: 92 additions & 0 deletions src/AzureClient/AzureSubmissionContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#nullable enable

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Quantum.IQSharp.Jupyter;
using Microsoft.Quantum.Runtime;

namespace Microsoft.Quantum.IQSharp.AzureClient
{
/// <summary>
/// Represents the configuration settings for a job submission to Azure Quantum.
/// </summary>
public sealed class AzureSubmissionContext : IQuantumMachineSubmissionContext
{
private static readonly int DefaultShots = 500;
private static readonly int DefaultExecutionTimeoutInSeconds = 30;
private static readonly int DefaultExecutionPollingIntervalInSeconds = 5;

internal static readonly string ParameterNameOperationName = "__operationName__";
internal static readonly string ParameterNameJobName = "jobName";
internal static readonly string ParameterNameShots = "shots";
internal static readonly string ParameterNameTimeout = "timeout";
internal static readonly string ParameterNamePollingInterval = "poll";

/// <inheritdoc/>
public string FriendlyName { get; set; } = string.Empty;

/// <inheritdoc/>
public int Shots { get; set; } = DefaultShots;

/// <summary>
/// The Q# operation name to be executed as part of this job.
/// </summary>
public string OperationName { get; set; } = string.Empty;

/// <summary>
/// The input parameters to be provided to the specified Q# operation.
/// </summary>
public Dictionary<string, string> InputParameters { get; set; } = new Dictionary<string, string>();

/// <summary>
/// The execution timeout for the job, expressed in seconds.
/// </summary>
/// <remarks>
/// This setting only applies to %azure.execute. It is ignored for %azure.submit.
/// The timeout determines how long the IQ# kernel will wait for the job to complete;
/// the Azure Quantum job itself will continue to execute until it is completed.
/// </remarks>
public int ExecutionTimeout { get; set; } = DefaultExecutionTimeoutInSeconds;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the plan for these two properties to connect up to %config, or will there be another way to set them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With these changes, the two properties can be set via timeout and poll parameters to the %azure.execute magic command. It’s worth discussing whether they should be on %config instead, but since they only apply to %azure.execute it seemed to make sense to put them here for now.


/// <summary>
/// The polling interval, in seconds, to check for job status updates
/// while waiting for an Azure Quantum job to complete execution.
/// </summary>
/// <remarks>
/// This setting only applies to %azure.execute. It is ignored for %azure.submit.
/// </remarks>
public int ExecutionPollingInterval { get; set; } = DefaultExecutionPollingIntervalInSeconds;

/// <summary>
/// Parses the input from a magic command into an <see cref="AzureSubmissionContext"/> object
/// suitable for job submission via <see cref="IAzureClient"/>.
/// </summary>
public static AzureSubmissionContext Parse(string inputCommand)
{
var inputParameters = AbstractMagic.ParseInputParameters(inputCommand, firstParameterInferredName: ParameterNameOperationName);
var operationName = inputParameters.DecodeParameter<string>(ParameterNameOperationName);
var jobName = inputParameters.DecodeParameter<string>(ParameterNameJobName, defaultValue: operationName);
var shots = inputParameters.DecodeParameter<int>(ParameterNameShots, defaultValue: DefaultShots);
var timeout = inputParameters.DecodeParameter<int>(ParameterNameTimeout, defaultValue: DefaultExecutionTimeoutInSeconds);
var pollingInterval = inputParameters.DecodeParameter<int>(ParameterNamePollingInterval, defaultValue: DefaultExecutionPollingIntervalInSeconds);

var decodedParameters = inputParameters.ToDictionary(
item => item.Key,
item => inputParameters.DecodeParameter<string>(item.Key));

return new AzureSubmissionContext()
{
FriendlyName = jobName,
Shots = shots,
OperationName = operationName,
InputParameters = decodedParameters,
ExecutionTimeout = timeout,
ExecutionPollingInterval = pollingInterval,
};
}
}
}
15 changes: 8 additions & 7 deletions src/AzureClient/EntryPoint/EntryPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,18 @@ public EntryPoint(object entryPointInfo, Type inputType, Type outputType, Operat
}

/// <inheritdoc/>
public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, Dictionary<string, string> inputParameters)
public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, AzureSubmissionContext submissionContext)
{
var parameterTypes = new List<Type>();
var parameterValues = new List<object>();
foreach (var parameter in OperationInfo.RoslynParameters)
{
if (!inputParameters.ContainsKey(parameter.Name))
if (!submissionContext.InputParameters.ContainsKey(parameter.Name))
{
throw new ArgumentException($"Required parameter {parameter.Name} was not specified.");
}

string rawParameterValue = inputParameters[parameter.Name];
string rawParameterValue = submissionContext.InputParameters[parameter.Name];
object? parameterValue = null;
try
{
Expand All @@ -73,17 +73,18 @@ public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, Dictionary<
};

// Find and invoke the method on IQuantumMachine that is declared as:
// Task<IQuantumMachineJob> SubmitAsync<TInput, TOutput>(EntryPointInfo<TInput, TOutput> info, TInput input)
// Task<IQuantumMachineJob> SubmitAsync<TInput, TOutput>(EntryPointInfo<TInput, TOutput> info, TInput input, SubmissionContext context)
var submitMethod = typeof(IQuantumMachine)
.GetMethods()
.Single(method =>
method.Name == "SubmitAsync"
&& method.IsGenericMethodDefinition
&& method.GetParameters().Length == 2
&& method.GetParameters().Length == 3
&& method.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == EntryPointInfo.GetType().GetGenericTypeDefinition()
&& method.GetParameters()[1].ParameterType.IsGenericMethodParameter)
&& method.GetParameters()[1].ParameterType.IsGenericMethodParameter
&& method.GetParameters()[2].ParameterType == typeof(IQuantumMachineSubmissionContext))
.MakeGenericMethod(new Type[] { InputType, OutputType });
var submitParameters = new object[] { EntryPointInfo, entryPointInput };
var submitParameters = new object[] { EntryPointInfo, entryPointInput, submissionContext };
return (Task<IQuantumMachineJob>)submitMethod.Invoke(machine, submitParameters);
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/AzureClient/EntryPoint/IEntryPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public interface IEntryPoint
/// Submits the entry point for execution to Azure Quantum.
/// </summary>
/// <param name="machine">The <see cref="IQuantumMachine"/> object representing the job submission target.</param>
/// <param name="inputParameters">The provided input parameters to the entry point operation.</param>
/// <param name="submissionContext">The <see cref="AzureSubmissionContext"/> object representing the submission context for the job.</param>
/// <returns>The details of the submitted job.</returns>
public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, Dictionary<string, string> inputParameters);
public Task<IQuantumMachineJob> SubmitAsync(IQuantumMachine machine, AzureSubmissionContext submissionContext);
}
}
4 changes: 2 additions & 2 deletions src/AzureClient/IAzureClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public Task<ExecutionResult> ConnectAsync(IChannel channel,
/// <returns>
/// Details of the submitted job, or an error if submission failed.
/// </returns>
public Task<ExecutionResult> SubmitJobAsync(IChannel channel, string operationName, Dictionary<string, string> inputParameters);
public Task<ExecutionResult> SubmitJobAsync(IChannel channel, AzureSubmissionContext submissionContext);

/// <summary>
/// Executes the specified Q# operation as a job to the currently active target
Expand All @@ -137,7 +137,7 @@ public Task<ExecutionResult> ConnectAsync(IChannel channel,
/// <returns>
/// The result of the executed job, or an error if execution failed.
/// </returns>
public Task<ExecutionResult> ExecuteJobAsync(IChannel channel, string operationName, Dictionary<string, string> inputParameters);
public Task<ExecutionResult> ExecuteJobAsync(IChannel channel, AzureSubmissionContext submissionContext);

/// <summary>
/// Sets the specified target for job submission.
Expand Down
14 changes: 2 additions & 12 deletions src/AzureClient/Magic/ExecuteMagic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#nullable enable

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Jupyter.Core;
Expand All @@ -15,8 +16,6 @@ namespace Microsoft.Quantum.IQSharp.AzureClient
/// </summary>
public class ExecuteMagic : AzureClientMagicBase
{
private const string ParameterNameOperationName = "operationName";

/// <summary>
/// Initializes a new instance of the <see cref="ExecuteMagic"/> class.
/// </summary>
Expand Down Expand Up @@ -59,16 +58,7 @@ The Azure Quantum workspace must previously have been initialized
/// </summary>
public override async Task<ExecutionResult> RunAsync(string input, IChannel channel)
{
var inputParameters = ParseInputParameters(input, firstParameterInferredName: ParameterNameOperationName);
var operationName = inputParameters.DecodeParameter<string>(ParameterNameOperationName);

var decodedParameters = new Dictionary<string, string>();
foreach (var key in inputParameters.Keys)
{
decodedParameters[key] = inputParameters.DecodeParameter<string>(key);
}

return await AzureClient.ExecuteJobAsync(channel, operationName, decodedParameters);
return await AzureClient.ExecuteJobAsync(channel, AzureSubmissionContext.Parse(input));
}
}
}
2 changes: 1 addition & 1 deletion src/AzureClient/Magic/OutputMagic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.Quantum.IQSharp.AzureClient
/// </summary>
public class OutputMagic : AzureClientMagicBase
{
private const string ParameterNameJobId = "jobId";
private const string ParameterNameJobId = "id";

/// <summary>
/// Initializes a new instance of the <see cref="OutputMagic"/> class.
Expand Down
2 changes: 1 addition & 1 deletion src/AzureClient/Magic/StatusMagic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Microsoft.Quantum.IQSharp.AzureClient
/// </summary>
public class StatusMagic : AzureClientMagicBase
{
private const string ParameterNameJobId = "jobId";
private const string ParameterNameJobId = "id";

/// <summary>
/// Initializes a new instance of the <see cref="StatusMagic"/> class.
Expand Down
13 changes: 1 addition & 12 deletions src/AzureClient/Magic/SubmitMagic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ namespace Microsoft.Quantum.IQSharp.AzureClient
/// </summary>
public class SubmitMagic : AzureClientMagicBase
{
private const string ParameterNameOperationName = "operationName";

/// <summary>
/// Initializes a new instance of the <see cref="SubmitMagic"/> class.
/// </summary>
Expand Down Expand Up @@ -56,16 +54,7 @@ The Azure Quantum workspace must previously have been initialized
/// </summary>
public override async Task<ExecutionResult> RunAsync(string input, IChannel channel)
{
var inputParameters = ParseInputParameters(input, firstParameterInferredName: ParameterNameOperationName);
var operationName = inputParameters.DecodeParameter<string>(ParameterNameOperationName);

var decodedParameters = new Dictionary<string, string>();
foreach (var key in inputParameters.Keys)
{
decodedParameters[key] = inputParameters.DecodeParameter<string>(key);
}

return await AzureClient.SubmitJobAsync(channel, operationName, decodedParameters);
return await AzureClient.SubmitJobAsync(channel, AzureSubmissionContext.Parse(input));
}
}
}
2 changes: 1 addition & 1 deletion src/Jupyter/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public static T DecodeParameter<T>(this Dictionary<string, string> parameters, s
{
return defaultValue;
}
return (T)(JsonConvert.DeserializeObject(parameterValue)) ?? defaultValue;
return (T)System.Convert.ChangeType(JsonConvert.DeserializeObject(parameterValue), typeof(T)) ?? defaultValue;
}
}
}
Loading