get python argparser attributes into script namespace - python

I used to do this in my python scripts:
import sys
my_int = int(sys.argv[1])
...more commands...
How can I replicate THIS EXACT functionality with the argparse module? Do I need to extract all of the variables from the parser? That would be tedious.
I don't want an answer like this:
if __name__ == "__main__":
parser = ArgumentParser()
parser.add_argument("--my_int", type=int, dest="my_int")
args = parser.parse_args()
main(**vars(args))
because I want to be able to interrupt my script in ipython and have all the variables still be defined.

locals().update(vars(args))
is the answer

Related

Passing arguments to a Python script

I want to pass arguments to a Python script in two ways,
python main.py --source=aws
and
python main.py source aws
This is my current code,
parser = argparse.ArgumentParser()
parser.add_argument("--s", "--source", help='Flag to choose source')
This makes the first option possible. How do I make the second option possible?
There is not a way to do that with Argparse. The only way to do that is by filtering stdin using sys.argv.
import argparse
import sys
mangle_my_args = ['s', 'source']
arguments=['--'+arg if arg in mangle_my_args else arg for arg in sys.argv[1:]]
parser = argparse.ArgumentParser()
parser.add_argument("--s", "--source", help='Flag to choose source')
print(parser.parse_args(arguments))

Python: How to make command line arguments available for objects?

I am relatively new to Python and I am trying to solve the following problem at the moment. I am writing a script which parses command line arguments with argparse. Besides some other things I have a verbosity flag, which I want to make available for some objects. Currently I am passing it to the constructor and I am wondering if there is a more elegant way for doing this.
class MyClass(object):
def __init__(self, verbosity=False):
self.verbosity = verbosity
def do_something(self):
if self.verbosity:
print("Doing something ...")
.
import argparse
import myclass
def main():
parser = argparse.ArgumentParser(description='Testing.')
parser.add_argument('-v', '--verbosity', action='store_true',
help="Produce verbose output")
args = parser.parse_args()
object = myclass.MyClass(verbosity=args.verbosity)
object.do_something()
if __name__ == "__main__":
main()
Any help would really be appreciated.
I would say what you're doing is the cleanest approach, at least in this simple case. You could pass the args namespace that argparse gives you to the MyClass initializer, but then you'll need to construct a similar object if you want to run tests on your class, or to use it as a library from another script.
If you want the same command line options to be available from multiple classes or scripts, you could create a config.py that looks something like this:
import argparse
__all__ = ['options']
parser = argparse.ArgumentParser(description='Testing.')
parser.add_argument('-v', '--verbosity', action='store_true',
help="Produce verbose output")
args = parser.parse_args()
options = vars(args)
Then from your script you can do from config import options and get the parsed argument from options['verbosity']. You could also change the value of options - for something like:
if self.too_much_output():
options['verbosity'] = False
and have that change be available to any code that's using options.
(The idea to use vars(args) comes from this answer to another argparse-related question.)

Use argparse module in python to parse a single argument

I used the OptParse module a few years back, and it seemed so easy, but seeing the argparse module, it doesn't seem intuitive to use. Thus, I'd like some help.
I currently have (which isnt much yet):
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
I'd like to be able to issue a command like python myscript.py --char 20 . This value for char flag will always be an int.
If someone can please help, I'd greatly appreciate it! Thank you
This is how you add an argument, and retrieve it:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--char', type=int,
help='number of characters')
args = parser.parse_args()
char = args.char
You should check out the docs:
https://docs.python.org/3/library/argparse.html
And here's a tutorial:
https://docs.python.org/2/howto/argparse.html
you need to add an argument to the parser object, and optionally specify the parameter type to be int
# testargs.py
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--char', type=int)
args = parser.parse_args()
print(args.char)
if you execute this file with
python testargs.py --char 20
it should print 20 to the console

Unittest with command-line arguments

From what I understand from another SO post, to unittest a script that takes command line arguments through argparse, I should do something like the code below, giving sys.argv[0] as arg.
import unittest
import match_loc
class test_method_main(unittest.TestCase):
loc = match_loc.main()
self.assertEqual(loc, [4])
if __name__ == '__main__':
sys.argv[1] = 'aaaac'
sys.argv[2] = 'ac'
unittest.main(sys.argv[0])
This returns the error:
usage: test_match_loc.py [-h] text patterns [patterns ...]
test_match_loc.py: error: the following arguments are required: text, patterns
I would like to understand what is going on here deeper. I understand
if __name__ == '__main__':
main()
says that if this is being executed by the 'main', highest level, default interpreter, to just automatically run the 'main' method. I'm assuming
if __name__ == '__main__':
unittest.main()
just happens to be the way you say this for running unittest scripts.
I understand when any script is run, it automatically has an argv object, a vector collecting all the items on the command line.
But I do not understand what unittest.main(sys.arg[0]) would do. What does 'unittest.main' do with arguments? How can I pre-set the values of sys.argv - doesn't it automatically reset every time you run a script? Furthermore, where does this object 'sys.argv' exist, if outside of any script? Finally, what is the correct way to implement tests of command-line arguments?
I am sorry if my questions are vague and misguided. I would like to understand all the components relevant here so I can actually understand what I am doing.
Thank you very much.
Just by playing around with a pair of simple files, I find that modifying sys.argv in the body of the caller module affects the sys.argv that the imported module sees:
import sys
sys.argv[1] = 'aaaac'
sys.argv[2] = 'ac'
class test_method_main(unittest.TestCase):
...
But modifying sys.argv in the main block as you do, does not show up in the imported one. We could dig into the documentation (and code) to see exactly why, but I think it's enough to just identify what works.
Here's what I reconstructed from your previous question of the imported module - with a few diagnostic prints
import argparse
import sys
def main():
print(sys.argv)
parser = argparse.ArgumentParser(
description='Takes a series of patterns as fasta files'
' or strings and a text as fasta file or string and'
' returns the match locations by constructing a trie.')
parser.add_argument('text')
parser.add_argument('patterns', nargs='+')
args = parser.parse_args()
print(args)
return 1
You could also test a parser with your own list of strings, recognising that parse_args uses sys.argv[1:] if its argument is missing or None:
def main(argv=None):
print(argv)
...
args = parser.parse_args(argv)
print(args)
return 1
loc = match_loc.main(['abc','ab']) # and in the caller
Even though I was able to construct a working test case, you really should have given enough information that I didn't need to guess or dig around.

Is there a way to add an already created parser as a subparser in argparse?

Normally, to add a subparser in argparse you have to do:
parser = ArgumentParser()
subparsers = parser.add_subparser()
subparser = subparsers.add_parser()
The problem I'm having is I'm trying to add another command line script, with its own parser, as a subcommand of my main script. Is there an easy way to do this?
EDIT: To clarify, I have a file script.py that looks something like this:
def initparser():
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
parser.add_argument('--bar')
return parser
def func(args):
#args is a Namespace, this function does stuff with it
if __name__ == '__main__':
initparser().parse_args()
So I can run this like:
python script.py --foo --bar
I'm trying to write a module app.py that's a command line interface with several subcommands, so i can run something like:
python app.py script --foo --bar
Rather than copy and pasting all of the initparser() logic over to app.py, I'd like to be able to directly use the parser i create from initparser() as a sub-parser. Is this possible?
You could use the parents parameter
p=argparse.ArgumentParser()
s=p.add_subparsers()
ss=s.add_parser('script',parents=[initparser()],add_help=False)
p.parse_args('script --foo sst'.split())
ss is a parser that shares all the arguments defined for initparser. The add_help=False is needed on either ss or initparser so -h is not defined twice.
You might want to take a look at the shlex module as it sounds to me like you're trying to hack the ArgumentParser to do something that it wasn't actually intended to do.
Having said that, it's a little difficult to figure out a good answer without examples of what it is, exactly, that you're trying to parse.
I think your problem can be addressed by a declarative wrapper for argparse. The one I wrote is called Argh. It helps with separating definition of commands (with all arguments-related stuff) from assembling (including subparsers) and dispatching.
This is a way old question, but I wanted to throw out another alternative. And that is to think in terms of inversion of control. By this I mean the root ArgumentParser would manage the creation of the subparsers:
# root_argparser.py
from argparse import ArgumentParser, Namespace
__ARG_PARSER = ArgumentParser('My Script')
__SUBPARSERS = __ARG_PARSER.add_subparsers(dest='subcommand')
__SUBPARSERS.required = True
def get_subparser(name: str, **kwargs) -> ArgumentParser:
return __SUBPARSERS.add_parser(name, **kwargs)
def parse_args(**kwargs) -> Namespace:
return __ARG_PARSER.parse_args(**kwargs)
# my_script.py
from argparse import ArgumentParser
from root_argparse import get_subparser
__ARG_PARSER = get_subparser('script')
__ARG_PARSER.add_argument('--foo')
__ARG_PARSER.add_argument('--bar')
def do_stuff(...):
...
# main.py
from root_argparse import parse_args
import my_script
if __name__ == '__main__':
args = parse_args()
# do stuff with args
Seems to work okay from some quick testing I did.

Categories