I am trying to run a windows executable via python3 and have below code snippet. The arguments can be of the form key-value pair but its possible that few arguments may not have value like arg1 below. arg2 needs to have a value which is passed as creating arg2 variable below. The data.filepath needs to be used to construct arg2
# data.filepath resolves to \\server1\data\inputs\filename.txt
arg2 = "--arg2 {}".format(data.filepath)
child = subprocess.Popen([output_path, "--arg1", arg2, "--arg3 val3", "--arg4 val4"],
shell=False, stderr=subprocess.STDOUT)
child.communicate()[0]
rc = child.returncode
But seems that i am not following correct syntax and getting error as below
Throw location unknown (consider using BOOST_THROW_EXCEPTION)
Dynamic exception type: class boost::exception_detail::clone_impl<struct boost::exception_detail::error_info_injector<class boost::program_options::unknown_option> >
std::exception::what: unrecognised option '--arg2 \\server1\data\inputs\filename.txt'
Please let me know the right syntax in python to pass arguments to executables properly.
Apparently, the program you are running expects to receive an argument and its value as separate strings (which makes a lot of sense). You can do something like
if phase_of_moon() == 'waxing gibbous':
arg2 = ['--arg2', data.filepath]
else:
arg2 = []
x = Popen([output_path, '--arg1', *arg2, '--arg3', val3])
using iterable unpacking to expand arg2.
Related
This is my script mytest.py.
import argparse
parser = argparse.ArgumentParser(description="Params")
parser.add_argument(
"--value")
def test(args):
print(args.value)
args = parser.parse_args()
test(args)
I want to pass argument store in variable val
val =1
!python mytest.py --value val
instead of printing 1 it print val. How to send 1 stored in variable val.
argparse always get argument as string, or list of strings on default, and what you do on your shell is irrelevant with python program. It is no wonder val is printed.
Use file that contains "1" and read that file to do what you intended to.
As jueon park said naming a variable in commandline wont work
It would create an error like the above one.If you are calling the command from any programer will work but in cmd it won't work
I'm very late for this but your python code works just fine. The problem you have is that you are not passing the arguments correctly.
For this to work first you need to correctly set the variable:
val=1
(note that the "=" must be next to both the variable name and the value)
and the you can simply use $ to get the value from the variable. So:
python mytest.py --value $val
In a shell script I have:
/usr/local/bin/pybot --variablefile variables.py:$var1:$var2 test_cases.tsv
inside variables.py how can I access var1 and var2 arguments?
I have tried:
import sys
var1 = sys.argv[1]
var1 = sys.argv[2]
it seems like this doesn't work.
For you to access the variables, your variable file must define the function get_variables, which will be given the arguments passed from the command line. This function needs to return a dictionary where the keys are the robot variable names.
For example:
def get_variables(arg1, arg2):
variables = {
"var1": arg1,
"var2": arg2
}
return variables
If your variable file is based on a class, the class needs to have the get_variables method.
For example:
# variables.py
class variables(object):
def get_variables(self, arg1, arg2):
variables = {
"var1": arg1,
"var2": arg2
}
return variables
When you do the above, your test will have two variables set: ${var1} and ${var2} which will have the values that were passed via the --variablefile argument.
Here is a test that can be used to verify the above:
# example.robot
*** Test cases ***
Example
should be equal ${var1} hello
should be equal ${var2} world
Here is how to run the test in order for it to pass:
$ var1=hello
$ var2=world
$ /usr/local/bin/pybot --variablefile variables.py:$var1:$var2 example.robot
Of course, var1 and var2 are completely arbitrary. You can pass raw strings, too:
$ /usr/local/bin/pybot --variablefile variables.py:hello:world example.robot
Passing arguments is described in the user guide section titled Getting variables from a special function
sys reads the arguments fron the command line, as they appears to it:
sys.argv[0] contains the script name
sys.argv[1], the first argument (whatever it is)
sys.argv[2], the second, and so on.
You should use argparse, it helps to build comprehensive CLIs. A nice tutorial exists on the Python website.
You seem to make assumptions about how the arguments are parsed which are not true. Here's how these arguments are passed from the shell to Python:
sys.argv[0] is /usr/local/bin/pybot
sys.argv[1] is --variablefile
sys.argv[2] is variables.py:$var1:$var2 where the values of the shell variables var1 and var2 are substituted.
sys.argv[n] is test_cases.tsv
The last one is [n] because without quotes around the argument, sys.argv[2] might actually be split into multiple values. For example, if var1 contains = foo * bar= then actually
sys.argv[2] is variables.py:=
sys.argv[3] is foo
sys.argv[4..n-2] is a list of files in the current directory, and
sys.argv[n-1] is =bar:$var2 where similar further processing for the value of var2 may take place.
There are Python argument parsing modules which assign further semantics e.g. to arguments which start with a dash (these will be interpreted as options) but by itself, Python does no such thing. If that's what you want, maybe look at argparse or one of its replacements; but you still need to understand how the basic mechanics work. A common arrangement is to avoid internal structure in arguments, and instead require the user to pass each value as a separate argument -- so perhaps
--variablefile variables.py --variablefile "$var1" --variablefile "$var2"
with quoting to prevent the shell from attempting to perform whitespace tokenization and wildcard expansion on the variable values, and then probably in your script an argparse definition which says to merge multiple option arguments into a list.
parser = argparse.ArgumentParser()
parser.add_argument('--variablefile', action='append')
in command line if I run my program
python parse.py config=abc.txt factor_date=20151001 like this
I want the position of argument will be fixed. That means if I pass argument like below
python parse.py factor_date=20151001 config=abc.txt
it has to show error.
import sys
config_file=sys.argv[1]
factor_date = sys.argv[2]
argstring=""+config_file+" "+factor_date+""
arg_list = argstring.split(' ')
input={}
for arg in arg_list:
#x=arg.split("--")
key,val=arg.split("=")[0],arg.split("=")[1]
if key == "config":
input[key]=val
if key =="factor_date":
input[key]=val
print input
You can have a look at click. It let's you create command line interfaces pretty much effortlessly. It's bases on using decorators.
You should have a look at argparse. Your use case is for positional arguments. If you specify the name of the argument (optional arguments with argparse) then it does not make sense to force a specific order.
Still, when using positional arguments one could call the program with worng arguments, you will have to check by yourself the values provided by the user. However, you can force a type and it will automagically convert the strings, which in the case you describe would solve the problem.
I have an argparsing program:
import argparse
def main():
p = argparse.ArgumentParser()
help_str = {'rule': 'Blah blah blah and stuff.'}
p.add_argument('-r', '--rule', action="store", type=str, dest='rule', help=help_str['rule'])
p.set_defaults(rule='string')
args = p.parse_args()
Therefore, my command line input:
username#compname:~$python progname.py -r/--rule 'something'
I am trying to figure out how I could just input:
username#compname:~$python progname.py -r/--rule
and have my own error message come up:
print '\n Error: no input value for rule:'
print ' resetting rule to default value.'
args.rule = 'string'
after this, the rule value should print out as 'string'
I am only slightly fluent in Python, sorry. I have tried using try/except blocks and even sys.argv[2] (though I may have done something wrong.) This is the error that keeps popping up with all of my solutions (not very familiar with errors outside Type and Value):
progname.py: error: argument -r/--rule: expected one argument
Any help appreciated.
p = argparse.ArgumentParser()
p.add_argument('-r', '--rule', nargs='?', default='string')
args = p.parse_args()
print args
with nargs='?' produces Namespace(rule='string') if called without argument. With -r, args is Namespace(rule=None). And Namespace(rule='something') with -r something.
Lets add a few lines of code
if args.rule is None:
print '\n Error: no input value for rule:'
print ' resetting rule to default value.'
args.rule = 'string'
print args
Now the output with '-r' (or --rule) is:
Namespace(rule=None)
Error: no input value for rule:
resetting rule to default value.
Namespace(rule='string')
the other cases are the same.
If I drop the default; p.add_argument('-r', '--rule', nargs='?'), the no argument case also produces this custom 'error message', since the default (in argparse) is None.
It's possible to add custom error checking with a custom type or action, but I think testing for None after using argparse is simpler, and easier to understand.
I'd suggest changing this error message to a warning. An Error usually terminates the program; a warning prints the message and continues.
Here's a solution when nargs=2 (or something else that's fixed). It's not a trivial change, since it involves redefining the error method (documented near the end of the argparse docs), and then capturing the error that it produces. The error method cannot access the Namespace (args), nor can it continue parse_args. So it cannot handle any other arguments if there is problem with the rule argument.
class MyParser(argparse.ArgumentParser):
def error(self, message):
if 'rule' in message:
message = 'wrong number of input values for rule'
raise argparse.ArgumentError(None,message)
# cannot access namespace or continue parsing
else:
super(MyParser, self).error(message)
p = MyParser()
p.add_argument('-r', '--rule', nargs=2)
try:
args = p.parse_args()
except argparse.ArgumentError as e:
print e
args = argparse.Namespace(rule='default')
# create a namespace with this default value
print args
Keep in mind that nargs has 2 purposes:
- raise an error if a wrong number of argument strings is given
- allocate multiple argument strings among multiple Actions (the thing created by add_argument).
The 2nd is especially evident if you have, for example, several positionals, taking 1,2 and * arguments respectively. It is a key novel feature of argparse, at least relative to earlier Python parsers. While it possible to tweak it, it is hard to completely redefine it.
If the arguments allow it, you could use nargs='*' instead of the '?', and issue the warning if the number of strings is not '2' (or whatever).
This first link has the same question in the first section, but it is unanswered
(python argparse: parameter=value). And this second question is similar, but I can't seem to get it working for my particular case
( Using argparse to parse arguments of form "arg= val").
So my situation is this -- I am re-writing a Python wrapper which is used by many other scripts (I would prefer not to modify these other scripts). Currently, the Python wrapper is called with command line arguments of the form --key=value for a number of different arguments, but was parsed manually. I would like to parse them with argparse.
N.B. The argument names are unwieldy, so I am renaming using the dest option in add_argument.
parser = argparse.ArgumentParser(description='Wrappin Ronnie Reagan')
parser.add_argument("--veryLongArgName1", nargs=1, dest="arg1", required=True)
parser.add_argument("--veryLongArgName2", nargs=1, dest="arg2")
parser.add_argument("--veryLongArgName3", nargs=1, dest="arg3")
userOpts = vars(parser.parse_args())
Which, while apparently parsing the passed command lines correctly, displays this as the help:
usage: testing_argsparse.py [-h] --veryLongArgName1 ARG1
[--veryLongArgName2 ARG2]
[--veryLongArgName3 ARG3]
testing_argsparse.py: error: argument --veryLongArgName1 is required
But what I want is that all parameters are specified with the --key=value format, not --key value. i.e.
usage: testing_argsparse.py [-h] --veryLongArgName1=ARG1
[--veryLongArgName2=ARG2]
[--veryLongArgName3=ARG3]
testing_argsparse.py: error: argument --veryLongArgName1 is required
testing_argsparse.py --veryLongArgName1=foo
works. argparse module accepts both --veryLongArgName1=foo and --veryLongArgName1 foo formats.
What exact command line arguments are you trying to pass to argparse that's causing it to not work?
A little late but for anyone with a similar request as the OP you could use a custom HelpFormatter.
class ArgFormatter(argparse.HelpFormatter):
def _format_args(self, *args):
result = super(ArgFormatter, self)._format_args(*args)
return result and '%%%' + result
def _format_actions_usage(self, *args):
result = super(ArgFormatter, self)._format_actions_usage(*args)
return result and result.replace(' %%%', '=')
This can then be passed to ArgumentParser to give the wanted behavior.
parser = argparse.ArgumentParser(
description='Wrappin Ronnie Reagan',
formatter_class=ArgFormatter)
This intercepts the args (ARG1, ARG2, ...) and adds a custom prefix which is later replaced (along with the unwanted space) for an = symbol. The and in the return statements makes sure to only modify the result if it's non-empty.