Skip to content

Commit e10eec6

Browse files
authored
The handler should always return an integer (#2092)
* the handler should always return an integer (exit code) * remove InvocationResult
1 parent 259d5a2 commit e10eec6

38 files changed

+160
-206
lines changed

src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ System.CommandLine
5050
public System.Collections.Generic.IEnumerator<Symbol> GetEnumerator()
5151
public ParseResult Parse(System.Collections.Generic.IReadOnlyList<System.String> args, CommandLineConfiguration configuration = null)
5252
public ParseResult Parse(System.String commandLine, CommandLineConfiguration configuration = null)
53-
public System.Void SetHandler(System.Action<System.CommandLine.Invocation.InvocationContext> handle)
54-
public System.Void SetHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task> handle)
53+
public System.Void SetHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Int32> handler)
54+
public System.Void SetHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Int32>> handler)
5555
public static class CommandExtensions
5656
public static System.Int32 Invoke(this Command command, System.String[] args, IConsole console = null)
5757
public static System.Int32 Invoke(this Command command, System.String commandLine, IConsole console = null)
@@ -69,7 +69,7 @@ System.CommandLine
6969
public CommandLineBuilder RegisterWithDotnetSuggest()
7070
public CommandLineBuilder UseDefaults()
7171
public CommandLineBuilder UseEnvironmentVariableDirective()
72-
public CommandLineBuilder UseExceptionHandler(System.Action<System.Exception,System.CommandLine.Invocation.InvocationContext> onException = null, System.Nullable<System.Int32> errorExitCode = null)
72+
public CommandLineBuilder UseExceptionHandler(System.Func<System.Exception,System.CommandLine.Invocation.InvocationContext,System.Int32> onException = null, System.Int32 errorExitCode = 1)
7373
public CommandLineBuilder UseHelp(System.Nullable<System.Int32> maxWidth = null)
7474
public CommandLineBuilder UseHelp(System.String name, System.String[] helpAliases)
7575
public CommandLineBuilder UseHelp(System.Action<System.CommandLine.Help.HelpContext> customize, System.Nullable<System.Int32> maxWidth = null)
@@ -104,10 +104,10 @@ System.CommandLine
104104
public static System.Void Write(this IConsole console, System.String value)
105105
public static System.Void WriteLine(this IConsole console, System.String value)
106106
public class Directive : Symbol
107-
.ctor(System.String name, System.Action<System.CommandLine.Invocation.InvocationContext> syncHandler = null, System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task> asyncHandler = null)
107+
.ctor(System.String name, System.Func<System.CommandLine.Invocation.InvocationContext,System.Int32> syncHandler = null, System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Int32>> asyncHandler = null)
108108
public System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem> GetCompletions(System.CommandLine.Completions.CompletionContext context)
109-
public System.Void SetAsynchronousHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task> handler)
110-
public System.Void SetSynchronousHandler(System.Action<System.CommandLine.Invocation.InvocationContext> handler)
109+
public System.Void SetAsynchronousHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task<System.Int32>> handler)
110+
public System.Void SetSynchronousHandler(System.Func<System.CommandLine.Invocation.InvocationContext,System.Int32> handler)
111111
public class EnvironmentVariablesDirective : Directive
112112
.ctor()
113113
public interface ICommandHandler
@@ -241,9 +241,7 @@ System.CommandLine.Invocation
241241
public class InvocationContext
242242
.ctor(System.CommandLine.ParseResult parseResult, System.CommandLine.IConsole console = null)
243243
public System.CommandLine.IConsole Console { get; set; }
244-
public System.Int32 ExitCode { get; set; }
245244
public System.CommandLine.Help.HelpBuilder HelpBuilder { get; }
246-
public System.Action<InvocationContext> InvocationResult { get; set; }
247245
public System.CommandLine.ParseResult ParseResult { get; set; }
248246
public T GetValue<T>(Option<T> option)
249247
public T GetValue<T>(Argument<T> argument)

src/System.CommandLine.Benchmarks/CommandLine/Perf_Parser_Simple.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ private static RootCommand BuildCommand()
3737
{
3838
bool boolean = ctx.ParseResult.GetValue(boolOption);
3939
string text = ctx.ParseResult.GetValue(stringOption);
40+
41+
return 0;
4042
});
4143

4244
return command;

src/System.CommandLine.Generator/Invocations/ConstructorModelBindingInvocation.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,17 @@ public override string InvokeContents()
4848
{
4949
case ReturnPattern.InvocationContextExitCode:
5050
builder.Append(@"
51-
return await global::System.Threading.Tasks.Task.FromResult(context.ExitCode);");
51+
return 0;");
5252
break;
5353
case ReturnPattern.FunctionReturnValue:
5454
builder.Append(@"
55-
return await global::System.Threading.Tasks.Task.FromResult(rv);");
55+
return rv;");
5656
break;
5757
case ReturnPattern.AwaitFunction:
5858
builder.Append(@"
5959
await rv;");
6060
builder.Append(@"
61-
return context.ExitCode;");
61+
return 0;");
6262
break;
6363
case ReturnPattern.AwaitFunctionReturnValue:
6464
builder.Append(@"

src/System.CommandLine.Generator/Invocations/DelegateInvocation.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,17 @@ public virtual string InvokeContents()
5050
{
5151
case ReturnPattern.InvocationContextExitCode:
5252
builder.Append(@"
53-
return await Task.FromResult(context.ExitCode);");
53+
return 0;");
5454
break;
5555
case ReturnPattern.FunctionReturnValue:
5656
builder.Append(@"
57-
return await Task.FromResult(rv);");
57+
return rv;");
5858
break;
5959
case ReturnPattern.AwaitFunction:
6060
builder.Append(@"
6161
await rv;");
6262
builder.Append(@"
63-
return context.ExitCode;");
63+
return 0;");
6464
break;
6565
case ReturnPattern.AwaitFunctionReturnValue:
6666
builder.Append(@"

src/System.CommandLine.Hosting/HostingExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public static CommandLineBuilder UseHost(this CommandLineBuilder builder,
3737
services.AddSingleton(invocation);
3838
services.AddSingleton(bindingContext);
3939
services.AddSingleton(invocation.Console);
40-
services.AddTransient(_ => invocation.InvocationResult);
4140
services.AddTransient(_ => invocation.ParseResult);
4241
});
4342
hostBuilder.UseInvocationLifetime(invocation);

src/System.CommandLine.NamingConventionBinder.Tests/ModelBindingCommandHandlerTests.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,16 +321,12 @@ public async Task Unexpected_return_types_result_in_exit_code_0_if_no_exception_
321321
private static void CaptureMethod<T>(T value, InvocationContext invocationContext)
322322
{
323323
BoundValueCapturer.Capture(value, invocationContext);
324-
325-
invocationContext.InvocationResult = ctx => BoundValueCapturer.Apply(ctx);
326324
}
327325

328326
private static Action<T, InvocationContext> CaptureDelegate<T>()
329327
=> (value, invocationContext) =>
330328
{
331329
BoundValueCapturer.Capture(value, invocationContext);
332-
333-
invocationContext.InvocationResult = ctx => BoundValueCapturer.Apply(ctx);
334330
};
335331

336332
private static class BoundValueCapturer

src/System.CommandLine.NamingConventionBinder/CommandHandler.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -611,13 +611,10 @@ internal static async Task<int> GetExitCodeAsync(object? returnValue, Invocation
611611
{
612612
case Task<int> exitCodeTask:
613613
return await exitCodeTask;
614-
case Task task:
615-
await task;
616-
return context.ExitCode;
617614
case int exitCode:
618615
return exitCode;
619616
default:
620-
return context.ExitCode;
617+
return 0;
621618
}
622619
}
623620
}

src/System.CommandLine.Suggest.Tests/EndToEndTestApp/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static async Task Main(string[] args)
3030
string cherry = ctx.ParseResult.GetValue(cherryOption);
3131
string durian = ctx.ParseResult.GetValue(durianOption);
3232

33-
return Task.CompletedTask;
33+
return Task.FromResult(0);
3434
});
3535

3636
var commandLine = new CommandLineBuilder(rootCommand)

src/System.CommandLine.Suggest/SuggestionDispatcher.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.CommandLine.Parsing;
88
using System.IO;
99
using System.Linq;
10+
using System.Threading;
1011
using System.Threading.Tasks;
1112

1213
namespace System.CommandLine.Suggest
@@ -31,6 +32,7 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug
3132
CompleteScriptCommand.SetHandler(context =>
3233
{
3334
SuggestionShellScriptHandler.Handle(context.Console, context.ParseResult.GetValue(shellTypeArgument));
35+
return 0;
3436
});
3537

3638
ListCommand = new Command("list")
@@ -48,7 +50,7 @@ public SuggestionDispatcher(ISuggestionRegistration suggestionRegistration, ISug
4850
ExecutableOption,
4951
PositionOption
5052
};
51-
GetCommand.SetHandler(context => Get(context));
53+
GetCommand.SetHandler(Get);
5254

5355
var commandPathOption = new Option<string>("--command-path") { Description = "The path to the command for which to register suggestions" };
5456

@@ -132,7 +134,7 @@ private void Register(
132134
}
133135
}
134136

135-
private Task<int> Get(InvocationContext context)
137+
private Task<int> Get(InvocationContext context, CancellationToken cancellationToken)
136138
{
137139
var parseResult = context.ParseResult;
138140
var commandPath = parseResult.GetValue(ExecutableOption);

src/System.CommandLine.Tests/ArgumentTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ public async Task Custom_argument_parser_is_only_called_once()
378378
};
379379

380380
var command = new RootCommand();
381-
command.SetHandler((ctx) => handlerWasCalled = true);
381+
command.SetHandler((ctx) => { handlerWasCalled = true; return 0; });
382382
command.Options.Add(option);
383383

384384
await command.InvokeAsync("--value 42");

src/System.CommandLine.Tests/Binding/SetHandlerTests.cs

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,6 @@ namespace System.CommandLine.Tests.Binding
1616
{
1717
public class SetHandlerTests
1818
{
19-
[Fact]
20-
public async Task Unexpected_return_types_result_in_exit_code_0_if_no_exception_was_thrown()
21-
{
22-
var wasCalled = false;
23-
24-
var command = new Command("wat");
25-
26-
var handle = (InvocationContext ctx, CancellationToken cancellationToken) =>
27-
{
28-
wasCalled = true;
29-
return Task.FromResult(new { NovelType = true });
30-
};
31-
32-
command.SetHandler(handle);
33-
34-
var exitCode = await command.InvokeAsync("");
35-
wasCalled.Should().BeTrue();
36-
exitCode.Should().Be(0);
37-
}
38-
3919
[Fact]
4020
public async Task When_User_Requests_Cancellation_Its_Reflected_By_The_Token_Passed_To_Handler()
4121
{
@@ -47,11 +27,11 @@ public async Task When_User_Requests_Cancellation_Its_Reflected_By_The_Token_Pas
4727
try
4828
{
4929
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
50-
context.ExitCode = ExpectedExitCode * -1;
30+
return ExpectedExitCode * -1;
5131
}
5232
catch (OperationCanceledException)
5333
{
54-
context.ExitCode = ExpectedExitCode;
34+
return ExpectedExitCode;
5535
}
5636
});
5737

src/System.CommandLine.Tests/EnvironmentVariableDirectiveTests.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public async Task Sets_environment_variable_to_value()
2323
{
2424
asserted = true;
2525
Environment.GetEnvironmentVariable(variable).Should().Be(value);
26+
return 0;
2627
});
2728

2829
var config = new CommandLineBuilder(rootCommand)
@@ -45,6 +46,7 @@ public async Task Trims_environment_variable_name()
4546
{
4647
asserted = true;
4748
Environment.GetEnvironmentVariable(variable).Should().Be(value);
49+
return 0;
4850
});
4951

5052
var config = new CommandLineBuilder(rootCommand)
@@ -67,6 +69,7 @@ public async Task Trims_environment_variable_value()
6769
{
6870
asserted = true;
6971
Environment.GetEnvironmentVariable(variable).Should().Be(value);
72+
return 0;
7073
});
7174

7275
var config = new CommandLineBuilder(rootCommand)
@@ -89,6 +92,7 @@ public async Task Sets_environment_variable_value_containing_equals_sign()
8992
{
9093
asserted = true;
9194
Environment.GetEnvironmentVariable(variable).Should().Be(value);
95+
return 0;
9296
});
9397

9498
var config = new CommandLineBuilder(rootCommand)
@@ -110,6 +114,7 @@ public async Task Ignores_environment_directive_without_equals_sign()
110114
{
111115
asserted = true;
112116
Environment.GetEnvironmentVariable(variable).Should().BeNull();
117+
return 0;
113118
});
114119

115120
var config = new CommandLineBuilder(rootCommand)
@@ -132,6 +137,7 @@ public static async Task Ignores_environment_directive_with_empty_variable_name(
132137
asserted = true;
133138
var env = Environment.GetEnvironmentVariables();
134139
env.Values.Cast<string>().Should().NotContain(value);
140+
return 0;
135141
});
136142

137143
var config = new CommandLineBuilder(rootCommand)
@@ -154,6 +160,7 @@ public static async Task Ignores_environment_directive_with_whitespace_variable_
154160
asserted = true;
155161
var env = Environment.GetEnvironmentVariables();
156162
env.Values.Cast<string>().Should().NotContain(value);
163+
return 0;
157164
});
158165

159166
var config = new CommandLineBuilder(rootCommand)

src/System.CommandLine.Tests/GlobalOptionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public void When_a_required_global_option_is_omitted_it_results_in_an_error()
2929
{
3030
var command = new Command("child");
3131
var rootCommand = new RootCommand { command };
32-
command.SetHandler((_) => { });
32+
command.SetHandler((_) => 0);
3333
var requiredOption = new Option<bool>("--i-must-be-set")
3434
{
3535
IsRequired = true,
@@ -69,7 +69,7 @@ public void When_a_required_global_option_is_present_on_child_of_command_it_was_
6969
{
7070
var command = new Command("child");
7171
var rootCommand = new RootCommand { command };
72-
command.SetHandler((_) => { });
72+
command.SetHandler((_) => 0);
7373
var requiredOption = new Option<bool>("--i-must-be-set")
7474
{
7575
IsRequired = true,

src/System.CommandLine.Tests/Invocation/CancelOnProcessTerminationTests.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,12 @@ private static Task<int> Program(string[] args)
6666
// and reason why we need a timeout on termination processing.
6767
CancellationToken token = infiniteDelay ? CancellationToken.None : cancellationToken;
6868
await Task.Delay(Timeout.InfiniteTimeSpan, token);
69+
70+
return 0;
6971
}
7072
catch (OperationCanceledException)
7173
{
72-
context.ExitCode = GracefulExitCode;
74+
return GracefulExitCode;
7375
}
7476
});
7577

src/System.CommandLine.Tests/Invocation/InvocationExtensionsTests.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public async Task RootCommand_InvokeAsync_returns_0_when_handler_is_successful()
5252
var wasCalled = false;
5353
var rootCommand = new RootCommand();
5454

55-
rootCommand.SetHandler((_) => wasCalled = true);
55+
rootCommand.SetHandler((_) => { wasCalled = true; return 0; });
5656

5757
var result = await rootCommand.InvokeAsync("");
5858

@@ -66,7 +66,7 @@ public void RootCommand_Invoke_returns_0_when_handler_is_successful()
6666
var wasCalled = false;
6767
var rootCommand = new RootCommand();
6868

69-
rootCommand.SetHandler((_) => wasCalled = true);
69+
rootCommand.SetHandler((_) => { wasCalled = true; return 0; });
7070

7171
int result = rootCommand.Invoke("");
7272

@@ -127,8 +127,7 @@ public async Task RootCommand_InvokeAsync_can_set_custom_result_code()
127127

128128
rootCommand.SetHandler((context, cancellationToken) =>
129129
{
130-
context.ExitCode = 123;
131-
return Task.CompletedTask;
130+
return Task.FromResult(123);
132131
});
133132

134133
var resultCode = await rootCommand.InvokeAsync("");
@@ -143,8 +142,7 @@ public void RootCommand_Invoke_can_set_custom_result_code()
143142

144143
rootCommand.SetHandler((context, cancellationToken) =>
145144
{
146-
context.ExitCode = 123;
147-
return Task.CompletedTask;
145+
return Task.FromResult(123);
148146
});
149147

150148
int resultCode = rootCommand.Invoke("");

src/System.CommandLine.Tests/Invocation/InvocationPipelineTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ public async Task InvokeAsync_chooses_the_appropriate_command()
3939
var secondWasCalled = false;
4040

4141
var first = new Command("first");
42-
first.SetHandler((_) => firstWasCalled = true);
42+
first.SetHandler((_) => { firstWasCalled = true; return 0; });
4343

4444
var second = new Command("second");
45-
second.SetHandler((_) => secondWasCalled = true);
45+
second.SetHandler((_) => { secondWasCalled = true; return 0; });
4646

4747
var config = new CommandLineBuilder(new RootCommand
4848
{
@@ -64,10 +64,10 @@ public void Invoke_chooses_the_appropriate_command()
6464
var secondWasCalled = false;
6565

6666
var first = new Command("first");
67-
first.SetHandler((_) => firstWasCalled = true);
67+
first.SetHandler((_) => { firstWasCalled = true; return 0; });
6868

6969
var second = new Command("second");
70-
second.SetHandler((_) => secondWasCalled = true);
70+
second.SetHandler((_) => { secondWasCalled = true; return 0; });
7171

7272
var config = new CommandLineBuilder(new RootCommand
7373
{
@@ -350,7 +350,7 @@ public async Task Middleware_can_throw_OperationCancelledException()
350350
{
351351
throw new OperationCanceledException(cancellationToken);
352352
})
353-
.UseExceptionHandler((ex, ctx) => ctx.ExitCode = ex is OperationCanceledException ? 123 : 456)
353+
.UseExceptionHandler((ex, ctx) => ex is OperationCanceledException ? 123 : 456)
354354
.Build();
355355

356356
int result = await config.InvokeAsync("the-command");

src/System.CommandLine.Tests/ParserTests.MultiplePositions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public void A_command_can_be_specified_in_more_than_one_position(
118118
string expectedParent)
119119
{
120120
var reusedCommand = new Command("reused");
121-
reusedCommand.SetHandler((_) => { });
121+
reusedCommand.SetHandler((_) => 0);
122122
reusedCommand.Add(new Option<string>("--the-option"));
123123

124124
var outer = new Command("outer")

0 commit comments

Comments
 (0)