Hi Mrtrix3 experts, I am writing a python script that uses your app.py python library.
I want to add an optional argument that will take some default value if no user-defined value is given, the typical argparse syntax would be something like
options.add_argument(’-example’, nargs=’?’, const=1000, type=int, help=‘example description’)
so that if a user used -example with no value specified afterwards, then app.args.example would have the value 1000, otherwise app.args.example would take a user specified value.
Is this possible using app.py? I get an Unrecognized Arguments error when I try to add arguments this way.
Setting a default value for a command-line option should work exactly as you’ve described, and behave exactly how
argparse intends. I copy&pasted your example, and it worked exactly as I would expect.
My suspicion is that you’re encountering a misuse of the command-line in combination with the use of
nargs='?'. However it takes a bit of explaining.
As described here, command-line options in MRtrix3 binary executables can be placed anywhere on the command-line relative to the compulsory arguments, as long as any compulsory inputs to those options are provided immediately following the argument. However, none of the MRtrix3 binaries have the capability of defining a command-line option that only optionally also takes a subsequent value input: this is because doing so introduces a fundamental ambiguity as to which entries on the command-line are input values to command-line options, and which are compulsory command-line arguments.
argparse does permit command-line options that optionally take a value as input. However, the only way to avoid this fundamental ambiguity is if such an option is defined after all compulsory arguments.
Here’s a set of examples that will hopefully make the concept more clear. I added your “
-example” command-line option as-is to
dwi2response fa dwi.mif response.txt
Default usage; works as expected.
dwi2response fa dwi.mif response.txt -example
Runs without issue; variable
app.args.example takes the default value.
dwi2response fa dwi.mif response.txt -example 1
Runs without issue; variable
app.args.example takes the value of 1.
dwi2response fa dwi.mif -example response.txt
Gives error: “
Error: argument -example: invalid int value: 'response.txt'”.
argparse sees a command-line entry after “
-example”, and assumes that that must be the input value for option
-example, but clearly has an issue converting that string to an integer.
dwi2response fa dwi.mif -example 1 response.txt
This actually works:
argparse knows that only one command-line entry can be used as input to “
-example”, so it assigns “1” to
-example and then infers that
response.txt must be the next compulsory command-line option. However use of “
nargs='+'” would fail in such an instance, as “
response.txt” would be assigned to
dwi2response would then complain that no path to the output file was provided.
While I didn’t duplicate your “
Unrecognized Argument” error message here, this error can nevertheless occur in similar circumstances where assignment of command-line entries as either compulsory arguments or optional inputs becomes ambiguous. Only providing optional inputs at the end of the command string, rather than in the middle, can at least remove the ambiguity to the point where
argparse will succeed; I would still personally consider this bad practise and would advise against command-line options that take a non-constant number of values, though that may just be @jdtournier’s indoctrination. One thing to pay closer attention to particularly with the “
Unrecognized Arguments” error is the specific entry that
argparse labels as unrecognized: Often it’s the last entry at the command-line, despite your new command-line option being specified toward the start of the command-line string, indicating that the differentiation between compulsory arguments and options is going awry.
If this doesn’t clarify the specific issue you’re encountering, you’ll need to provide enough details in order for me to be able to duplicate the issue exactly.
Thanks so much, your explanation is perfectly clear. If it is bad practice to assign command line options a non-constant number of values, then is the good practice solution to assign multiple command line options? I would just assign one option to turn “-example” on with a default value and another option to allow users to specify their own -example value if they want to.
It depends on the details really, I don’t know that there’s a single hard answer. For instance, if there’s a “recommended” default value for an optional input, you could add that to the option’s help description, rather than defining it as a default value for if the user provides the option string but no trailing value. Alternatively you can have separate options for enabling a functionality v.s. manually controlling that functionality, as you describe. Sometimes it’s a matter of trying to think of all the ways that an independent user may mis-interpret your interface, and how you can alter your design to minimise such cases.