Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
colombod committed Jan 28, 2023
1 parent 81c2e98 commit 16b3610
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 63 deletions.
19 changes: 17 additions & 2 deletions src/Microsoft.DotNet.Interactive.Tests/SetMagicCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ await composite.SendAsync(new SubmitCode($@"
}

[Fact]
public async Task set_value_using_return_value_fails_when_there_is_no_ReturnValueProduced()
public async Task set_value_using_return_value_fails_when_successful_code_does_not_produce_return_value()
{
using var kernel = CreateKernel(Language.CSharp).UseSet();

Expand All @@ -98,10 +98,25 @@ public async Task set_value_using_return_value_fails_when_there_is_no_ReturnValu
var events = results.KernelEvents.ToSubscribedList();

events.Should().ContainSingle<CommandFailed>()
.Which.Message.Should().Be("The command was expected to produce a ReturnValueProduced event.");
.Which.Message.Should().Be("The submission did not produce a return value.");

}

[Fact]
public async Task does_not_set_value_if_the_submission_fails()
{
using var kernel = CreateKernel(Language.CSharp).UseSet();

var results = await kernel.SendAsync(new SubmitCode(@"
#!set --name x --from-result
throw new Exception(""custom error."");"));

var events = results.KernelEvents.ToSubscribedList();

events.Should().ContainSingle<CommandFailed>()
.Which.Message.Should().Contain("System.Exception: custom error.");
}

[Fact]
public async Task set_does_not_allow_from_value_and_from_results_at_the_same_time()
{
Expand Down
157 changes: 101 additions & 56 deletions src/Microsoft.DotNet.Interactive/KernelExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,7 @@ public static T UseLogMagicCommand<T>(this T kernel)

public static T UseSet<T>(this T kernel) where T : Kernel
{


var fromResultOption = new Option<bool?>(
var fromResultOption = new Option<bool>(
"--from-result",
description:"Captures the execution result.");

Expand Down Expand Up @@ -258,63 +256,12 @@ public static T UseSet<T>(this T kernel) where T : Kernel
{
nameOption,
fromResultOption,
fromValueOption
fromValueOption,
};

set.SetHandler(cmdLineContext =>
{
var fromResult = cmdLineContext.ParseResult.GetValueForOption(fromResultOption);
var valueName = cmdLineContext.ParseResult.GetValueForOption(nameOption);
var context = cmdLineContext.GetService<KernelInvocationContext>();
if (fromResult is true)
{
var returnValueProducedEvents = new List<ReturnValueProduced>();
var eventSubscription = kernel.KernelEvents.OfType<ReturnValueProduced>()
.Subscribe(e => returnValueProducedEvents.Add(e));
context.OnComplete((c) =>
{
eventSubscription.Dispose();
var returnValueProduced = returnValueProducedEvents.SingleOrDefault();
if (returnValueProduced is { })
{
if (kernel.SupportsCommandType(typeof(SendValue)))
{
kernel.SendAsync(
new SendValue(
valueName,
returnValueProduced.Value,
returnValueProduced.FormattedValues.FirstOrDefault()))
.GetAwaiter().GetResult();
}
else
{
c.Fail(c.Command, new CommandNotSupportedException(typeof(SendValue), kernel));
}
}
else
{
c.Fail(c.Command, message: "The command was expected to produce a ReturnValueProduced event.");
}
});
}
else
{
var fromValue = cmdLineContext.ParseResult.GetValueForOption(fromValueOption);
if (kernel.SupportsCommandType(typeof(SendValue)))
{
kernel.SendAsync(
new SendValue(
valueName,
fromValue))
.GetAwaiter().GetResult();
}
else
{
context.Fail(context.Command, new CommandNotSupportedException(typeof(SendValue), kernel));
}
}
HandleSetMagicCommand(kernel, cmdLineContext, fromResultOption, nameOption, fromValueOption);
});

kernel.AddDirective(set);
Expand Down Expand Up @@ -352,6 +299,104 @@ bool SetErrorIfNoneAreUsed( ArgumentResult result, params Option[] options)
}
}

private static void HandleSetMagicCommand<T>(
T kernel,
InvocationContext cmdLineContext,
Option<bool> fromResultOption,
Option<string> nameOption,
Option<string> fromValueOption)
where T : Kernel
{
var fromResult = cmdLineContext.ParseResult.GetValueForOption(fromResultOption);
var valueName = cmdLineContext.ParseResult.GetValueForOption(nameOption);
var context = cmdLineContext.GetService<KernelInvocationContext>();

if (fromResult)
{
context.OnComplete((c) =>
{
if (c.IsFailed)
{
return;
}
SetValueFromReturnValueProduced(c);
});
}
else
{
SetValueFromValueProduced();
}

void SetValueFromReturnValueProduced(KernelInvocationContext c)
{
var returnValueProducedEvents = new List<ReturnValueProduced>();
using var eventSubscription = context.KernelEvents.OfType<ReturnValueProduced>()
.Subscribe(e => returnValueProducedEvents.Add(e));

var returnValueProduced = returnValueProducedEvents.SingleOrDefault();

if (returnValueProduced is { })
{
if (kernel.SupportsCommandType(typeof(SendValue)))
{
kernel.SendAsync(
new SendValue(
valueName,
returnValueProduced.Value,
returnValueProduced.FormattedValues.FirstOrDefault()))
.GetAwaiter().GetResult();
}
else
{
c.Fail(c.Command, new CommandNotSupportedException(typeof(SendValue), kernel));
}
}
else
{
c.Fail(c.Command, message: "The submission did not produce a return value.");
}
}

void SetValueFromValueProduced()
{
if (kernel.SupportsCommandType(typeof(SendValue)))
{
var events = new List<ValueProduced>();

using var subscription = context.KernelEvents.OfType<ValueProduced>().Subscribe(e => events.Add(e));

var valueProduced = events.SingleOrDefault();


if (valueProduced is { })
{
kernel.SendAsync(
new SendValue(
valueName,
valueProduced.Value,
valueProduced.FormattedValue
))
.GetAwaiter().GetResult();
}
else
{
var interpolatedValue = cmdLineContext.ParseResult.GetValueForOption(fromValueOption);
kernel.SendAsync(
new SendValue(
valueName,
interpolatedValue
))
.GetAwaiter().GetResult();
}
}
else
{
context.Fail(context.Command, new CommandNotSupportedException(typeof(SendValue), kernel));
}
}
}

public static T UseValueSharing<T>(this T kernel) where T : Kernel
{
var sourceValueNameArg = new Argument<string>(
Expand Down
9 changes: 4 additions & 5 deletions src/Microsoft.DotNet.Interactive/KernelInvocationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ namespace Microsoft.DotNet.Interactive;

public class KernelInvocationContext : IDisposable
{
private bool _isFailed;

private static readonly AsyncLocal<KernelInvocationContext> _current = new();

private readonly ReplaySubject<KernelEvent> _events = new();
Expand Down Expand Up @@ -68,6 +66,7 @@ private KernelInvocationContext(KernelCommand command)

_disposables.Add(operation);
}
internal bool IsFailed { get; private set; }

public KernelCommand Command { get; }

Expand All @@ -79,7 +78,7 @@ private KernelInvocationContext(KernelCommand command)

public void Complete(KernelCommand command)
{
SucceedOrFail(!_isFailed, command);
SucceedOrFail(!IsFailed, command);
}

public void Fail(
Expand Down Expand Up @@ -118,7 +117,7 @@ private void SucceedOrFail(

var completingMainCommand = CommandEqualityComparer.Instance.Equals(command, Command);

if (succeed && !_isFailed)
if (succeed && !IsFailed)
{
if (completingMainCommand)
{
Expand All @@ -145,7 +144,7 @@ private void SucceedOrFail(

TryCancel();

_isFailed = true;
IsFailed = true;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@
<NamedTestSelector>
<TestName>Microsoft.DotNet.Interactive.App.Tests.StdioConnectionTests.fast_path_commands_over_proxy_can_be_handled</TestName>
</NamedTestSelector>
<NamedTestSelector>
<TestName>StdIoBehaviorTests.Quit_command_causes_stdio_process_to_end</TestName>
</NamedTestSelector>
</IgnoredTests>
</Settings>
</ProjectConfiguration>

0 comments on commit 16b3610

Please sign in to comment.