Postion of argument will be fixed - python

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.

Related

argparse nargs and positionals

This is a program that already exists and I'm trying to extend it - so my hands are tied somewhat :-(.
I have a program where I want to add an option that takes an unknown number of values so I'm trying to use nargs='+'. I can spot when a value is not actually for my option and is a positional argument and I can then use setattr to set the positional argument - but argparse doesn't get a chance to find the positional argument itself so complains.
The syntax for the command, as show in arparse generated help text, is
command [--option value [value...]] positional
In theory this is possible if I did this instead
command positional [--option value [value...]]
This is precisely how examples, even in the argparse documentation, work but that is NOT how the command is currently used, NOT how users typically provide programs with options and NOT how argparse generated help text shows the expected syntax.
So it there a way to somehow both handle the positional but also tell argparse 'oh, I found this positional so no need to complain that it is missing'?

Run tests from code with arg

I used to run tests with a command like
pytest.main('-s path_to_file --my_fixtures_arg1 arg_value')
At the moment, such a call is considered obsolete, and you need to call this command through a list of arguments. But I can not pass the necessary parameters in any way. Who knows the solution?
I'm currently using list of arguments since passing string was deprecated. In your case I think this one should work:
arguments = ['-s', '--my_fixtures_arg1=arg_value', 'path_to_file']
pytest.main(args=arguments)
Currently pytest uses shlex.split to split a string into a list of arguments.
You can do the same if your string comes from DB, config file or such:
import shlex, sys
args_list = shlex.split(args_string, posix=sys.platform != "win32")

argparse: how to parse a single string argument OR a file listing many arguments?

I have a use case where I'd like the user to be able to provide, as an argument to argparse, EITHER a single string OR a filename where each line has a string.
Assume the user launches ./myscript.py -i foobar
The logical flow I'm looking for is something like this:
The script determines whether the string foobar is a readable file.
IF it is indeed a readable file, we call some function from the script, passing each line in foobar as an argument to that function. If foobar is not a readable file, we call the same function but just use the string foobar as the argument and return.
I have no ability to guarantee that a filename argument will have a specific extension (or even an extension at all).
Is there a more pythonic way to do this OTHER than just coding up the logic exactly as I've described above? I looked through the argparse tutorial and didn't see anything, but it also seems reasonable to think that there would be some specific hooks for filenames as arguments, so I figured I'd ask.
A way would be:
Let's say that you have created a parser like this:
parser.add_argument('-i',
help='...',
type=function)
Where type points to the function which will be an outer function that evaluates the input of the user and decides if it is a string or a filename
More information about type you can find in the documentation.
Here is a minimal example that demonstrates this use of type:
parser.add_argument('-d','--directory',
type=Val_dir,
help='...')
# ....
def Val_dir(dir):
if not os.path.isdir(dir):
raise argparse.ArgumentTypeError('The directory you specified does not seem to exist!')
else:
return dir
The above example shows that with type we can control the input at parsing time. Of course in your case the function would implement another logic - evaluate if the input is a string or a filename.
This doesn't look like an argparse problem, since all you want from it is a string. That string can be a filename or a function argument. To a parser these will look the same. Also argparse isn't normally used to run functions. It is used to parse the commandline. Your code determines what to do with that information.
So here's a script (untested) that I think does your task:
import argparse
def somefunction(*args):
print(args)
if __name__=='__main__':
parser=argparse.ArgumentParser()
parser.add_argument('-i','--input')
args = parser.parse_args()
try:
with open(args.input) as f:
lines = f.read()
somefunction(*lines)
# or
# for line in lines:
# somefuncion(line.strip())
except:
somefunction(arg.input)
argparse just provides the args.input string. It's the try/except block that determines how it is used.
================
Here's a prefix char approach:
parser=argparse.ArgumentParser(fromfile_prefix_chars='#',
description="use <prog -i #filename> to load values from file")
parser.add_argument('-i','--inputs')
args=parser.parse_args()
for arg in args.inputs:
somefunction(arg)
this is supposed to work with a file like:
one
two
three
https://docs.python.org/3/library/argparse.html#fromfile-prefix-chars

How do I configure required arguments while still allowing special arguments like "--version"?

I have a Python script that accepts one or more input files and produces one or more output files (sort of a compiler, translating one syntax into another)
In my argparse section, I have configured so that the list of input files option is "nargs='+'", so that it will show a "too few arguments" error if user provides zero input files.
At the same time, I want to have a "--version" option that will just print the current script version and exit. When this option is provided, everything else (if provided) is irrelevant and should be ignored.
Just like ArgumentParser automatically adds the "--help" option which works like this, how can I add a "--version" option without changing the nargs='+' mechanism?
Try the version action class. From the docs:
'version' - This expects a version= keyword argument in the add_argument() call, and prints version information and exits when invoked:
>>>
>>> import argparse
>>> parser = argparse.ArgumentParser(prog='PROG')
>>> parser.add_argument('--version', action='version', version='%(prog)s 2.0')
>>> parser.parse_args(['--version'])
PROG 2.0
It behaves like the help (-h) except it displays the version parameter that you define with it (or lacking that a version value that you give the parser itself).

argparse optional argument before positional argument

I was wondering if it is possible to have a positional argument follow an argument with an optional parameter. Ideally the last argument entered into the command line would always apply toward 'testname'.
import argparse
parser = argparse.ArgumentParser(description='TAF')
parser.add_argument('-r','--release',nargs='?',dest='release',default='trunk')
parser.add_argument('testname',nargs='+')
args = parser.parse_args()
I would like both of these calls to have smoketest apply to testname, but the second one results in an error.
>> python TAF.py -r 1.0 smoketest
>> python TAF.py -r smoketest
TAF.py: error: too few arguments
I realize that moving the positional argument to the front would result in the correct behavior of the optional parameter, however this is not quite the format I am looking for. The choices flag looks like an attractive alternative, however it throws an error instead of ignoring the unmatched item.
EDIT:
I've found a hacky way around this. If anyone has a nicer solution I would appreciate it.
import argparse
parser = argparse.ArgumentParser(description='TAF')
parser.add_argument('-r','--release',nargs='?',dest='release',default='trunk')
parser.add_argument('testname',nargs=argparse.REMAINDER)
args = parser.parse_args()
if not args.testname:
args.testname = args.release
args.release = ''
As stated in the documentation:
'?'. One argument will be consumed from the command line if possible,
and produced as a single item. If no command-line argument is present,
the value from default will be produced. Note that for optional
arguments, there is an additional case - the option string is present
but not followed by a command-line argument. In this case the value
from const will be produced.
So, the behaviour you want is not obtainable using '?'. Probably you could write some hack using argparse.Action and meddling with the previous results.(1)
I think the better solution is to split the functionality of that option. Make it an option that requires an argument(but the option itself is optional) and add an option without argument that sets the release to 'trunk'. In this way you can obtain the same results without any hack. Also I think the interface is simpler.
In your example:
python TAF.py -r smoketest
It's quite clear that smoketest will be interpreted as an argument to -r. At least following unix conventions. If you want to keep nargs='?' then the user must use --:
$ python TAF.py -r -- sometest
Namespace(release=None, testname=['sometest']) #parsed result
(1) An idea on how to do this: check if the option has an argument. If it has one check if it is a valid test name. If so put into by hand into testname and set release to the default value. You'll also have to set a "flag" that tells you that this thing happened.
Now, before parsing sys.argv you must redirect sys.stderr. When doing the parsing you must catch SystemExit, check the stderr and see if the error was "too few arguments", check if the flag was set, if so ignore the error and continue running, otherwise you should reprint to the original stderr the error message and exit.
This approach does not look robust, and it's probably buggy.

Categories