I need to pass arguments directly to a command executed in my python script. The script itself does not need to evaluate or manipulate the arguments to be passed to the command.
./cookbooks.py vendor -o "--delete"
Unfortunately, argparse recognizes the -- in the quoted string as an attempt to provide an optional argument instead of a value and an error is returned, "error: argument -o/--options: expected one argument". It considers no value was provided for -o.
I simply want to append whatever is passed and not hinder the operators ability to use the underlying command. In below snippet, the options variable is supposed to be the quoted value of whatever.
cmd = ' '.join(["berks", "vendor", dir, options])
subprocess.call(cmd, cwd=entry.path, shell=True)
My parse argument is defined as below. I have also tried with type=str.
parser.add_argument("-o", "--options",
help="berks vendor or knife cookbook upload options passed directly to the command")
Is it possible to indicate to argparse to accept quoted values as is?
With your current definition, you can call your script with
./cookbooks.py vendor -o=--delete
Since -- doesn't start an argument, argparse doesn't see it as an option, only as part of a name-value pair to split on =.
Related
Is it possible to use more than one name to refer to a single argument in argparse?
Specifically, I want to allow user to specify an argument by either underscore and hyphen (dash)?
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--input-file')
args = parser.parse_args()
For example, I want both of the following options to work for the argument args.input_file:
python main.py --input-file /tmp/file
python main.py --input_file /tmp/file
Simply listing more options in .add_argument():
arg_parser.add_argument('--input-file', '--input_file')
should do the trick.
Note that using a minus - character in your argument is the preferred GNU syntax.
I have the following snippet:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--p', required=True)
parser.add_argument('arg', action=MyAction)
parser.parse_args()
, where MyAction is a simple custom action class.
As you see, I want to enforce the presence of the p argument. However, my action is performed even if the argument is not present, and then an error message is shown that indicates the fact the the argument is missing.
Obviously, I could check in my action class for the presence of the p argument, but this defies the purpose of having a required parameter in the first place. Why is my action being run if the argument is not present?
Parsing is driven by the commandline strings, and tries to be order agnostic. Within those rules, it alternates between parsing an optional and a positional.
For example, with myprog --p one two:
'--p' - pass the 'one' string to the p action (e.g. setattr(namespace, 'p', 'one')
'two' - matches the nargs for 'arg'. Calls your MyAction.__call__ with values='one'.
at the end of parsing it checks if all required actions have been 'seen'. With your setup both '--p' and 'arg' are required.
With myprog two --p one it does the same, except arg is processed first. The namespace may have a default value for p.
With myprog two, arg is processed, and the required test will raise an error. error: the following arguments are required: --p
Since you have written a custom Action, you can easily explore how the namespace contents vary depending on the commandline arguments and their order.
So the --p and the arg will be processed independently, and in either order, depending on the commandline strings. required testing is performed at the end, using a list of seen_actions. And default values are set at the start of parsing. It is difficult to implement reliable inter-action tests within custom Actions. Usually it is better to perform such tests after parsing.
The defined Actions only change the args namespace. So parsing does not change anything beyond what it returns. Unless there's an error and it forces an sys.exit. A custom MyAction class can change that, but at your own risk.
I try to build an argparser, where one of the parsers should have a default value, and is also a required.
I have the following so far:
fulllist_parser.add_argument(
'--type',
required=True,
default="VirtualMachine",
type=str,
help='Object type, e.g. Network, VirtualMachine.'
When I run it from CLI, I get an error:
supdeploy fulllist: error: argument --type is required
I see why this is coming up, as I'm not including --type on the CLI.
And that is what I want to achieve, that the default is set even if I don't include the parser option on CLI.
Anyway to run this?
You just have to use default and no required
fulllist_parser.add_argument(
'--type',
default="VirtualMachine",
type=str,
help='Object type, e.g. Network, VirtualMachine.'
Because then if it isn't specified you get the default value back (so you always get a value). Have a look at the documentation:
The default keyword argument of add_argument(), whose value defaults to None, specifies what value should be used if the command-line argument is not present. For optional arguments, the default value is used when the option string was not present at the command line
I want to use '=' as argument separator and didn't get any option in library documentation. So, '=' supported as a argument separator/deliminator by argparse.
class Parse:
def __init__(self):
parser = argparse.ArgumentParser()
parser.add_argument("script_config",help="Script Config File")
parser.add_argument("devices",help="devices")
parser.add_argument("log_file",help="log_file")
parser.add_argument("result_file",help="result_file")
parser.add_argument("testbed_file",help="testbed_file")
parser.add_argument("runtime",help="Just Runetime")
args = parser.parse_args()
print pprint.pprint(args)
a=Parse()
Output to above code, Here I got runtime as runtime=10.
root#ip-:~/cloudzelera/$ python ../lib/TestsuiteOption.py /tmp/abc.conf qa05__lnx1__i-12b651ea /tmp/123.suite /tmp/result.tmp /tmp/config runtime=10
Namespace(devices='qa05__lnx1__i-12b651ea', log_file='/tmp/123.suite', result_file='/tmp/result.tmp', runtime='runtime=10', script_config='/tmp/abc.conf', testbed_file='/tmp/config')
None
runtime is not an optional argument, it is a required, positional argument. As such you'd never use the name on the command line:
TestsuiteOption.py /tmp/abc.conf qa05__lnx1__i-12b651ea /tmp/123.suite /tmp/result.tmp /tmp/config 10
If you want runtime to be optional, start the option with two dashes (for a long name):
parser.add_argument("--runtime", help="Just Runtime")
and use the same on the command line:
TestsuiteOption.py /tmp/abc.conf --runtime=10 qa05__lnx1__i-12b651ea /tmp/123.suite /tmp/result.tmp /tmp/config
Now the option can be used anywhere on the command line, including at the start.
Note that argparse uses the UNIX convention of command line arguments, where optional arguments start with - for short 1-character arguments, -- for long arguments. It is not suitable for other conventions.
Given that what you have specified is not compliant with the Unix argument parsing conventions, why have another line at the end where you would do:
args.runtime = args.runtime.split('=')[1]
I cannot figure out this behaviour of argparse from the documentation:
import argparse
parser.add_argument("--host", metavar="", dest="host", nargs=1, default="localhost", help="Name of host for database. Default is 'localhost'.")
args = parser.parse_args()
print(args)
Here is the output with and without an argument for "--host":
>> python demo.py
Namespace(host='localhost')
>> python demo.py --host host
Namespace(host=['host'])
In particular: why does the argument to "--host" get stored in a list when it is specified but not when the default is used?
Remove the "nargs" keyword argument. Once that argument is defined argparse assumes your argument is a list (nargs=1 meaning a list with 1 element)
As an alternative and handy module: Docopt can be used for parsing command line arguments. Docopt transform a commandline into a dictionnary by defining values inside doc.
The question title and the question body ask two different questions. This is potentially a sign of the confusion I shared with the OP.
The title: why is the default a string not a list?
The body: why is the given value a list not a string?
The selected answer provides the solution to the question in the body. The answer to the question in the title is:
The entry default="localhost" sets default to "localhost", which is a sting. To set it as a list, you could use: default=["localhost"].