adamabdelhamed / powerargs Goto Github PK
View Code? Open in Web Editor NEWThe ultimate .NET Standard command line argument parser
License: MIT License
The ultimate .NET Standard command line argument parser
License: MIT License
there is currently no license, could you add one? Because, you know, lack of license means no one can legally use it :-)
Usage is always shown for all actions. It would be useful to able to show usage for a single action either by asking for usage explicitly or when arguments for a known action are invalid.
After upgrading, Resource/DefaultBrowserUsageTemplate.html appears in my project.
The new ArgUsage.GenerateUsageFromTemplate<T>()
works fine without it so this file is noise. Can it be removed from the package?
When PowerArgs/ArgUsage.cs was rewritten in 0910357 the whitespace was removed inadvertently. In the latest code, the opening parentheses is missing the whitespace.
PowerArgs/ArgUsage.cs - June 4 (Lines 359-365)
if (inlineAliasInfo != string.Empty) inlineAliasInfo = "(" + inlineAliasInfo + ")";
rows.Add(new List<ConsoleString>()
{
new ConsoleString("-")+(usageInfo.Name + inlineAliasInfo),
descriptionString,
});
PowerArgs/ArgUsage.cs - May 24 (Lines 343-347)
rows.Add(new List<ConsoleString>()
{
new ConsoleString(indicator)+(usageInfo.Name + (usageInfo.Aliases.Count > 0 ? " ("+ usageInfo.Aliases[0] +")" : "")),
descriptionString,
});
This would be for "hidden" "secret" parameters, that I want parsed, but not displayed in the usage print out.
Perhaps an attribute [ArgHidden] or something like that...
In comparison to the old ArgUsage.GetStyledUsage<T>
, the default rendering from the new ArgUsage.GenerateUsageFromTemplate<T>()
has several shortcomings:
It'd be nice if Enum parameters also got assigned single letters to abbreviate each enum member.
For instance
name.exe /help /other
should either be illegal or the "/" should be interpreted as "-".
However they are just ignored.
My actions and arguments are not displayed in lower case and additionally, the action has a strange dash at the end of the line:
Adam,
Nice work. I found your PowerArgs while looking for a library to parse internal command line strings. The various command line parsing libraries all work on the Main Args list. It would seem that PowerArgs could be enhanced to handle any string even before it is broken into the Args list. That way the library could be used inside an application to handle an internal command driven interface. Any thoughts?
dgp
I just found that when I build PowerArgs in release, I get a TargetInvocationException (and NullReferenceException inner exception) from the AttrOverride.cs Set method, at this line here:
if (callingMethod.Name.StartsWith("set_") == false || callingMethod.IsSpecialName == false)
I have been told it's happening because:
StackTrace stackTrace = new StackTrace();
var callingMethod = stackTrace.GetFrame(1).GetMethod() as MethodInfo;
There are 7 references to this method, in CommandLineArgument.cs and CommandLineAction.cs
StackTrace is also used in the Get method of AttrOverride.cs but at the moment, it isn't being inlined (though the compiler is within its rights to at some point in the future!)
One workaround is to always build debug and release without optimisation.
Another is to decorate all of the calling members' sets like this:
public string Description
{
get
{
return overrides.Get<ArgDescription, string>(Metadata, d => d.Description, string.Empty);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)]
set
{
overrides.Set(value);
}
}
With either of these methods, it runs fine.
What might be a better long-term solution is getting the name using CallerMemberName instead of stackTrace.GetFrame():
http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute(v=vs.110).aspx
(This is .Net 4.5 though)
Could you recognize arrays of enums?
e.g.
OptionsEnum[] arg1
How do you set up the help for a parser that uses the Action Framework?
If I paraphrase the superencoder code snippets, what I'd want/expect is something does:
superencoder -help
-- displays the help docs
superencoder encode -help
-- displays EncodeArgs help docs
superencoder clip -help
-- displays ClipArgs help docs
At the moment, what I actually get is the following behaviour:
superencoder -help
-- displays the help docs and the error "No action was specified"
superencoder encode -help
-- displays EncodeArgs help docs and the error "The argument 'Source' is required"
superencoder clip -help
-- displays ClipArgs help docs and the error "The argument 'Source' is required"
So basically:
Cheers!
This seems to be an issue with 1.7.0.0 and 1.8.0.0.
I tested a usage message generated from the code given in the Advanced Example of the readme and this was the output.
Usage: ConsoleApplication22 options
OPTION TYPE POSITION DESCRIPTION
-action string* 0 Either encode or clip
-encodeargs encodeargs NA Encode a new video file
-clipargs clipargs NA Save a portion of a video to a new video file
EXAMPLE: superencoder encode fromFile toFile -encoder Wmv
Encode the file at 'fromFile' to an AVI at 'toFile'
The expected output (and how it functions in 1.6.0.0) is
Usage: ConsoleApplication22 <action> options
EXAMPLE: superencoder encode fromFile toFile -encoder Wmv
Encode the file at 'fromFile' to an AVI at 'toFile'
Actions:
encode - Encode a new video file
OPTION TYPE POSITION DESCRIPTION
-source (-s) string* 1 The source video file
-output (-o) string 2 Output file. If not specfied, defaults to current directory
-encoder (-e) encoder NA The type of encoder to use
clip - Save a portion of a video to a new video file
OPTION TYPE POSITION DESCRIPTION
-source (-so) string* 1 The source video file
-output (-ou) string 2 Output file. If not specfied, defaults to current directory
-from (-f) double NA The starting point of the video, in seconds
-to (-t) double NA The ending point of the video, in seconds
-encoder (-en) encoder NA The type of encoder to use
A feature that I'd love to see in PowerArgs is the ability to require arguments by group.
For example, let's say my console application declares four switch arguments: A, B, C and D. I want to require users to choose exactly two switches: one from A/B and one from C/D. The following combinations would be valid:
myapp.exe -a -c
myapp.exe -a -d
myapp.exe -b -c
myapp.exe -b -d
But the following would NOT be valid:
myapp.exe -a -b
myapp.exe -c -d
This is a feature that CommmandLineParser supports via the MutuallyExclusiveSet property, and which prevents me from switching over to PA.
It would be great if there were some kind of option to put the argument parsing in "non interative mode" for applications running outside of a UI context, that will supress PromptIfMissing
and TabCompletion
.
C:\> MyApp.exe -noninteractive otheroptions
GetUsage<T>
doesn't allow to fully modify usage string. There is a need to get less detailed output in scriptcs/scriptcs
- without type and position information.
Should we just add detailed
parameter (true
by default) to the GetUsage<T>
, so if it's set - type and position information should be included, otherwise - not.
If this solution is suitable I can make a PR with it.
How would I go about creating a custom help hook, that opened up a URL instead of writing to console, for example?
I created UrlHelpHook : HelpHook and overrode the AfterCancel event, but whichever one I call, it always performs both the HelpHook AfterCancel and the UrlHelpHook AfterCancel.
[AttributeUsage(AttributeTargets.Property|AttributeTargets.Parameter)]
public class UrlHelpHookAttribute : HelpHook
{
private string helpUrl;
public UrlHelpHookAttribute(string url)
:base()
{
this.helpUrl = Path.GetFullPath(url);
if (!File.Exists(this.helpUrl))
{
throw new ValidationArgException("File not found - " + url, new FileNotFoundException());
}
}
public override void AfterCancel(HookContext context)
{
if (WriteHelp == false) return;
Console.WriteLine(@"Launching default browser to display HTML ...");
Process.Start(this.helpUrl);
}
}
With custom revivers, the common way to convert types in .NET is to use do the following:
Was hopping this API could support these kind of conversions.
I have an args class marked with [TabCompletion]. My console application seems to start fine in interactive mode from a command prompt but raises an exception when started from PowerShell (3.0 running from PowerShell ISE)
Unhandled Exception:
System.IO.IOException: The handle is invalid.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
at System.Console.get_CursorLeft()
at PowerArgs.StdConsoleProvider.get_CursorLeft()
at PowerArgs.ConsoleHelper.ReadLine(String& rawInput, List`1 history, ITabCompletionSource[] tabCompletionHooks)
at PowerArgs.TabCompletion.BeforeParse(HookContext context)
at PowerArgs.ArgHook.HookContext.<RunBeforeParse>b__5(ArgHook h)
at PowerArgs.ArgHook.HookContext.RunHook(Func`2 orderby, Action`1 hookAction)
at PowerArgs.ArgHook.HookContext.RunBeforeParse()
at PowerArgs.Args.ParseInternal(CommandLineArgumentsDefinition definition, String[] input)
at PowerArgs.Args.ParseInternal[T](String[] input)
at PowerArgs.Args.<>c__DisplayClass1`1.<ParseAction>b__0()
at PowerArgs.Args.Execute[T](Func`1 argsProcessingCode)
at PowerArgs.Args.ParseAction[T](String[] args)
at PowerArgs.Args.<>c__DisplayClass11`1.<InvokeAction>b__10(String[] a)
at PowerArgs.REPL.DriveREPL[T](TabCompletion t, Func`2 eval, String[] args)
at PowerArgs.Args.<>c__DisplayClass11`1.<InvokeAction>b__f()
at PowerArgs.Args.Execute[T](Func`1 argsProcessingCode)
at PowerArgs.Args.InvokeAction[T](String[] args)
at TurfSport.BackOffice.Services.Console.Program.Main(String[] args) in C:\svn\turfsport\TurfSportBackOffice\TurfSport4\trunk\TurfSport.BackOffice.Services.Console\Program.cs:line 26
[TabCompletion]
[ArgExceptionBehavior(ArgExceptionPolicy.StandardExceptionHandling)]
[ArgExample("info", "About info")]
public class ServiceActions
{
[ArgActionMethod, ArgDescription("Get info about the service console.")]
public void Info(Actions.Info info)
{
info.DoAction();
}
}
And I am invoking using
private static void Main(string[] args)
{
log4net.Config.XmlConfigurator.Configure();
Args.InvokeAction<ServiceActions>(args);
}
If the user adds extra parameters that don't match the argument class's properties then they are ignored. However, an ArgException should be thrown.
This would work a-la Console.WriteLine() and automatically append a carriage return at the end of the string when writing it to the console
First off - nice work, this is the best Args parser I could find for c#. Here my suggestion:
I end up replicating something like this:
if (args.Length > 0) {
try {
var cliParameter = PowerArgs.Args.Parse<CliParameter>(args);
if (cliParameter.Help) {
Console.WriteLine( CliParameter.Description );
PowerArgs.ArgUsage.GetStyledUsage<CliParameter>().Write();
return;
}
# Do more work here ....
return;
}
catch (PowerArgs.ArgException)
{
;
}
}
PowerArgs.ArgUsage.GetStyledUsage<CliParameter>().Write();
How about adding:
Any thoughts?
Hi,
Is ArgIgnoreCase honoured when using an enum as a property. I know System.Enum.TryParseEnum allows you to pass whether to ignore case.
Regards,
Alex
Currently the ArgShortcutAttribute is set to AllowMultiple=false. This prevents multiple shortcuts for the same arg.
I'd like to do something like this.
[ArgShortcut("h")]
[ArgShortcut("?")]
public bool Help { get; set; }
Allowing a user to specify -help, -h OR -?.
The default layout is not always appropriate for every level of user. Customisation hooks would allow usage layout to be modified based on specific project/user needs.
I have a BaseArgs
class and a bunch of derived specialized argument classes. The BaseArgs
class has an Enum for the action to be invoked. Actions are complex enough to warrant their own specific classes to hold their args.
I was hoping to use Args.Parse<BaseArgs>(args)
to get the Action enum, then based on the result, use Args.Parse<DerivedArgs>(args)
, since DerivedArgs
's type corresponds to the Action I got from the previous call. Is there a way to do this, or would I have to just try to parse as each derived class and catch the exceptions myself?
I would like to see support for the deferred action methods to be placed in multiple, separate classes which should promote better separation of concerns and cleaner code.
In other words doing this:
[ArgActionType(AActions)]
[ArgActionType(BActions)]
public class MainActions { ... }
public class AActions { ... }
public class BActions { ... }
Or allow for multiple types in a single attribute such as:
[ArgActionType(AActions, BActions)]
public class MainActions { ... }
public class AActions { ... }
public class BActions { ... }
The obsolete GetStyledUsage
displays shortcuts in addition to the long-form argument names, but the new GenerateUsageFromTemplate
does not. I couldn't find any documentation on how to compose a template or find alternate options besides the default, so for now I have to go back to the obsolete GetStyledUsage
and suppress the compiler warning. :-)
First let me say how awesome this lib is and thank you for it!
one feature idea i have is that I would really like to be able to say
parsed.AnArge.WasProvidedByUser()
naturally the method call can be anything you want it to be but the feature is something i would like to have.
this is partly because i currently have 3 date arguments, start and end date or an as of date. either start and end date must be provided or as of date so i am not able to mark them all as required. looking into solving this with a custom validator but it seems like i will still not be able to leverage the prompt if not provided attribute so if you can come up with another solution that would allow that, maybe an argument dependency option.
[ArgRequiredWith(ArgClass.OtherArg)]
[ArgRequireIfOtherArgumentNotProvided(ArgClass.OtherArg)]
thanks again for everything!
Would be fantastic to be able to use a "double dash" for options, or sub commands of a single dash.
I.e.
LogQuery -log --html -dst:C:\la\
The : would also be great :)
--Brendan
Could you please strongly sign your DLL PowerArgs.dll
When called via a unit test, GetUsage()'s call to Assembly.GetEntryAssembly() returns null which causes a null reference exception when trying to access the assembly's location.
The following test code illustrates the problem:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PowerArgs;
namespace ArgsTests
{
[TestClass]
public class ArgUsageTests
{
[TestMethod]
public void TestArgUsage()
{
string[] args = new string[] { "test" };
var commandArgs = Args.Parse<SomeArgs>(args);
string usage = ArgUsage.GetUsage<SomeArgs>();
Assert.IsTrue(usage.Contains("Usage"));
}
}
public class SomeArgs
{
[ArgDescription("File name")]
[ArgPosition(0)]
public string FileName { get; set; }
}
}
A working class with action methods is now throwing exceptions after upgrading from 1.8.? to 2.0.?. Neither the class containing the action methods nor the class invoking it has changed. Is the syntax new, or are new attributes necessary? (I didn't see any new arguments to the ArgActionMethod() attribute constructor...)
Will try rolling back to 1.8 for now.
In many command line parsers I see custom DefaultValueAttribute or similar but I can set default values for properties in constructor of my args class. And my question is: what is purpose of DefaultValueAttribute? Do we really need it? One reason in my head is API beauty because we set default values near others metadata like Required.
In current version, ArgException is used for almost every exceptional situation - for args parsing errors, validating errors, and so on. Even such code, that's related to library using, uses ArgException:
var assembly = Assembly.GetEntryAssembly();
if (assembly == null)
{
throw new ArgException("PowerArgs could not determine the name of your executable automatically. This may happen if you run GetUsage<T>() from within unit tests. Use GetUsageT>(string exeName) in unit tests to avoid this exception.");
}
The main issue that ArgException isn't useful, when it comes to proper error handling. For example:
try
{
var parsed = Args.Parse<MyArgs>(args);
Console.WriteLine("You entered string '{0}' and int '{1}'", parsed.StringArg, parsed.IntArg);
}
catch (ArgException ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ArgUsage.GetUsage<MyArgs>());
}
Here we can't react properly - we just don't have information about exceptional situation - just a string message and that's all. We have 2 ways: to write message to the user (can be kinda cryptic for him) or to parse the string message (so we can know more about exceptional situation and react to it in other ways).
Solving this issue should involve exceptional situtions handling refactoring and creating of some exceptions hierarchy for the goods of library API.
It would be really useful to have more powerful support for enum features, specifically (in order of importance):
It would be great to somehow get a list of "unmatched" arguments when calling Args.Parse<T>
. For scriptcs, this would be useful for passing those parameters further down to the running script (see scriptcs #173).
This could be done in many ways, i.e.:
Args.Parse<T>
that takes a callback for unmatched argument handling:var args = Args.Parse<MyArguments>(args, unmatchedArgument =>
{
// Do something with the unmatchedArgument, i.e. pass it to script...
}
UnmatchedArgumentException
that has a list of arguments (could be part of #11):try
{
var args = Args.Parse<MyArguments>(args);
}
catch (UnmatchedArgumentException ex)
{
// Do something with ex.UnmatchedArguments...
}
TryParse<T>
method, that returns false
when an argument couldn't be matched with a property and adds the arguments to the list.var unmatchedArguments = new List<KeyValuePair<string, string>>();
if (!Args.TryParse<MyArguments>(out unmatchedArguments, args))
{
// Do something with unmatchedArguments
}
I personally like the first solution. Is this something that can be achieved easily? I'd be happy to do the work ๐
Long descriptions are not handled properly:
If one of my properties is of type Enum "Category", in the help text displayed, I get a very useful list of the categories available:
If I decorate this enum with the [Flags] attribute, and specify that multiple categories can be selected, I can change the type of my property to Category[] and PowerArgs is clever enough to parse a comma separated list of categories, and store them against my property.
But
In the help text, it no longer displays the list of Categories the user can choose from:
I don't think this is a Reviver issue, so is there a way to keep that functionality in?
At the moment, the Type of an argument isn't picked up very well if the type is generic.
The example I came across, is DateTime?
i.e. Nullable<DateTime>
This is displayed as nullable'1
Hi,
Would it be possible to set-up arguments as commands like in linux. For example "apt-get install" and then the command can have options (which you have already), though it would be nice if the long version of the options could be treated with a -- rather than -.
You can look at http://linux.die.net/man/8/apt-get to see what I mean.
Regards,
Alex
Hi,
I have an app that uses PowerArgs. The usage looks like the following:
Usage: spec options
OPTION TYPE POSITION DESCRIPTION
-path (-p) String 0 The path to search all the Spec assemblies. The default is spec.
-example (-e) String NA The example to execute. This could be a spec, example group or an e
xample.
-pattern (-P) String NA Load files matching pattern. The default is Specs.
-output (-o) String NA The output path of the test results. The default is test-results.xm
l.
-format (-f) ConsoleFormatterType NA Specifies what format to use for output.
-dryrun (-d) Switch NA Invokes formatters without executing the examples.
-version (-v) Switch NA Display the version.
-colour (-c) Switch NA Enable colour in the output.
-help (-h) Switch NA You're looking at it.
EXAMPLE: spec.(exe|sh) spec
Execute all the specs in the folder spec.
I use to be able to run the program as spec.exe artifacts -P Unit.Specs. Now I get
Unexpected Argument: artifacts
Now if I run spec.exe -p artifacts -P Unit.Specs it tells me
Argument specified more than once: path
So this is treating it as -p and -P as the same option, which I am guessing is incorrect.
The only way for me to get it working is to specify the long form:
spec.exe -path artifacts -pattern Unit.Specs
Hopefully this highlights my issues:
The cause is:
In ValidateArgScaffold
method, ArgRevivers.CanRevive
is called with the type of the property. But ArgRevivers.CanRevive
loads custom revivers by searching the assembly of the type passed in. So if the type and custom reviver are not defined in the same assembly, the custom reviver cannot be found.
I encountered the problem when using System.Net.Mail.MailAddress as argument type. The code is pasted here to help you debug the issue: (in F#)
type MailAddressReviver()=
[<ArgReviver>]
static member Revive (key:string) (address:string) =
new System.Net.Mail.MailAddress(address)
[<TabCompletion>]
type MyArgs()=
[<ArgDescription("Show this help information")>]
[<ArgShortcut("h")>]
member val Help = false with get, set
[<ArgDescription("Mail to")>]
[<ArgShortcut("mt")>]
member val MailTo:System.Net.Mail.MailAddress = null with get, set
In the ActionFrameworkV2 tests, I clearly see parameterless actions being invoked, but get the above-named exception when I attempt this myself. Has this feature not made it into the current Nuget version yet? (I'm using 1.8)
How are you supposed to use Args.Parse together with HelpHook?
I've seen this example:
var parsed = Args.Parse<MyArgs>(args);
if (parsed.Help)
{
ArgUsage.GetStyledUsage<MyArgs>().Write();
}
but it seems to make the HelpHook redundant.
For example:
From my Program.Main, I call this:
try
{
var parsed = Args.Parse<MyArgs>(args);
Console.WriteLine("Your name is {0}", parsed.Name);
}
catch(ArgException ex)
{
ArgUsage.GetStyledUsage<MyArgs>().Write();
Console.WriteLine(ex.Message);
}
I have a bool property Help, and it is decorated with the HelpHook attribute.
If I pass the argument -help (or -?) it runs the AfterCancel method of HelpHook, but the ArgCancelProcessingException eventually gets caught by:
private static T Execute<T>(Func<T> argsProcessingCode) where T : class
{
executeRecursionCounter++;
ArgHook.HookContext.Current = new ArgHook.HookContext();
try
{
return argsProcessingCode();
}
catch (ArgCancelProcessingException ex)
{
if (executeRecursionCounter > 1)
{
throw;
}
return CreateEmptyResult<T>(ArgHook.HookContext.Current, cancelled: true);
}
and CreateEmptyResult returns null, later giving "Object reference not set to an instance of an object" when it tries to access parsed.Name.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.