Python: Pass arguments from argparse to imported scripts - python

I'm building a command line tool which executes some python-scripts (k2_fig1 - k2_fig3) in one main *.py-file (let's call it "main_file.py"). In this "main_file.py" the user has to fill in some parameters for the database connection (username, dbname, etc.)
Now I don't know how to pass these parameters to every single python-script I am importing. What do I have to code to these imported files?
This is my code of the "main_file.py":
import argparse
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-D', '--database', action="store", type=str, dest="my_dbname", required=True, help="DB name")
parser.add_argument('-U', '--username', action="store", type=str, dest="my_username", required=True, help="DB username")
args = parser.parse_args()
# Import different scripts
import k2_fig1
import k2_fig2
import k2_fig3
if __name__ == '__main__':
main()

Without knowing anything else about k2fig_1 et al., you'll need to call them using subprocess rather than importing them.
import argparse
import subprocess
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-D', '--database', action="store", type=str, dest="my_dbname", required=True, help="DB name")
parser.add_argument('-U', '--username', action="store", type=str, dest="my_username", required=True, help="DB username")
args = parser.parse_args()
for script in ['k2_fig1', 'k2_fig2', 'k2_fig3']:
subprocess.call([script, '-U', args.my_username, '-D', args.my_dbname])
if __name__ == '__main__':
main()

I think the best way is to copy the namespace attributes to a "config" module::
import argparse
from . import config
from . import other
def update_obj(dst, src):
for key, value in src.items():
setattr(dst, key, value)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-D', '--database')
parser.add_argument('-U', '--username')
args = parser.parse_args('-D foo'.split())
update_obj(config, args)
And the "other module"::
from . import config
def some_func():
assert config.database == 'foo'

Related

Import module with argaprse and while using argparse in script

Im trying to import a module that contains argparse options to my main script. My main script also has argspars that need to pass a required argument. What is the best approach to do this?
here is an example:
script_a
import argparse
import sys
def option():
parser = argparse.ArgumentParser()
parser.add_argument('-a', '--all', help="this is all")
parser.add_argument('-b', '--other', help="this is other")
args = parser.parse_args()
return args
variable = "name"
def foo(variable):
options = option()
if options.all =='all':
result = f"the result is all with {variable}"
else:
result = f"the result is other with {variable}"
return result
def main():
test = foo(variable)
print(test)
Im importing my script1 to my main script, so basically merging the arguments together.
main_script
import argparse
import sys
import scriptA
def option():
parser = argparse.ArgumentParser()
parser.add_argument('-i', '--info', help="Info.", required = True)
arg = parser.parse_args()
return arg
def main():
scriptA.main()
if __name__ == "__main__":
main()
I want to have the arguments for both script in the command line. like so,
python main_script.py -i info -a all

Call a module containing command line arguments from another module

Consider a following python files, is there any way to pass cl arguments toother module on import? (calling os.system is not desired)
# A.py
if __name__ == "__main__":
# -- resolve command line arguments
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, required=True)
parser.add_argument('--out_file', type=str, required=True)
args = parser.parse_args()
# -- do some operations
# -- save results in `out_file`
#B.py
import A
# how to pass `name` and `out_file` in main?
The correct way is of course to change A.py to have a main function taking arguments as parameters as you were suggested in other answers.
So you really should use:
A.py:
# A.py
def main(args):
# -- resolve command line arguments
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, required=True)
parser.add_argument('--out_file', type=str, required=True)
args = parser.parse_args(args)
# -- do some operations
# -- save results in `out_file`
if __name__ == "__main__":
main(sys.argv)
B.py:
import A
import sys
A.main([sys.argv[0], '--name', 'NAME_X', '--out_file', 'FILE.YY'])
That being said, sys.argv is mutable, so it is possible to change it before calling ArgumentParser.parse_args.
So this is possible (even if a bit more hacky):
A.py:
# A.py
def main():
# -- resolve command line arguments
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, required=True)
parser.add_argument('--out_file', type=str, required=True)
args = parser.parse_args() # always use sys.argv
# -- do some operations
# -- save results in `out_file`
if __name__ == "__main__":
main()
B.py:
import A
import sys
sys.argv = [sys.argv[0], '--name', 'NAME_X', '--out_file', 'FILE.YY'])
A.main()
# A.py
def main():
# -- resolve command line arguments
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, required=True)
parser.add_argument('--out_file', type=str, required=True)
args = parser.parse_args()
# -- do some operations
# -- save results in `out_file`
return out_file
if __name__ == "__main__":
main()
#B.py
import A
def main():
out_file = A.main()
# how to pass `name` and `out_file` in main?
In a.py you need to move the main stuff to a function, for example a def main(). I also added an arguments=None parameter to main() that receives the args from b.py.
# a.py
import argparse
def main(arguments=None):
# -- resolve command line arguments
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, required=True)
parser.add_argument('--out_file', type=str, required=True)
args = parser.parse_args(arguments)
# -- do some operations
# -- save results in `out_file`
if __name__ == "__main__":
main()
And then you can pass arguments to that function in b.py like so
#b.py
from a import main
main(['--name', 'some_name', '--out_file', 'file.txt'])

argparse.ArgumentParser ArgumentError when adding arguments in multiple modules

I am working on automated test framework (using pytest) to test multiple flavors of an application. The test framework should be able to parse common (to all flavors) command line args and args specific to a flavor.
Here is how the code looks like:
parent.py:
import argparse
ARGS = None
PARSER = argparse.ArgumentParser()
PARSER.add_argument('--arg1', default='arg1', type=str, help='test arg1')
PARSER.add_argument('--arg2', default='arg2', type=str, help='test arg2')
def get_args():
global ARGS
if not ARGS:
ARGS = PARSER.parse_args()
return ARGS
MainScript.py:
import pytest
from parent import PARSER
ARGS = None
PARSER.conflict_handler = "resolve"
PARSER.add_argument('--arg3', default='arg3', type=str)
def get_args():
global ARGS
if not ARGS:
ARGS = PARSER.parse_args()
return ARGS
get_args()
def main():
pytest.main(['./Test_Cases.py', '-v'])
if __name__ == "__main__":
main()
Test_Cases.py
from MainScript import get_args
ARGS = get_args()
def test_case_one():
pass
Executing MainScript.py fails with following error:
E ArgumentError: argument --arg3: conflicting option string(s): --arg3
So the problem is that you have declared
PARSER.add_argument('--arg3', default='arg3', type=str)
in a global scope inside MainScript.py. That means that that line of code will be executed every time you import it like you do in Test_Cases.py hence why you get the conflict error, you're adding arg 3 to your argparse twice.
Easiest solution is to move PARSER.add_argument('--arg3', default='arg3', type=str) into your main() function as that will only get called once.
def main():
PARSER.add_argument('--arg3', default='arg3', type=str)
pytest.main(['./Test_Cases.py', '-v'])
But doing that causes another problem stemming from your multiple definition of get_args(). When you call get_args() before your main() it only has the two defined arguments from parent.py so it's missing arg3. If you move the call down into your main() or at least after your main() gets called it will work.
Personally I just removed both the definition and the call of get_args() from MainScript.py and it worked just fine.

How do I wrap argparse options into the python class?

I am pretty new to python OOP, so I got some confusion.
Currently I have:
parser = argparse.ArgumentParser(description='script 1.0')
parser.add_argument('-a', '--foo', help='specify foo')
parser.add_argument('-b', '--bar', type=int, help='specify bar')
parser.add_argument('-c', '--baz', help='specify baz')
parser.add_argument('-d', '--bar2', help='bar2')
args = parser.parse_args()
foo = args.foo
bar = args.bar
baz = args.baz
bar2 = args.bar2
which works pretty well, but I wan to create a class for the whole of my script and make argparse as a class's method (is it possible at all?).
So I tried:
import argparse
....
Class Program:
def __init__(self, foo, bar, baz, bar2):
self.foo = foo
self.bar = bar
...(so on for each arg)
def main():
parser = argparse.ArgumentParser(description='script 1.0')
parser.add_argument('-a', '--foo', help='specify foo')
parser.add_argument('-b', '--bar', type=int, help='specify bar')
parser.add_argument('-c', '--baz', help='specify baz')
parser.add_argument('-d', '--bar2', help='bar2')
args = parser.parse_args()
foo = self.foo
bar = self.bar
baz = self.baz
bar2 = self.bar2
I do not think I am doing right, though. I have not found too much info about it but one post on SO which did not clarify situation to me, so I want to have opinions for my specific case
argparse is already makes use of classes. argparse.ArgumentParser(...) creates a parser object. parser.add_argument(...) creates an Action object, and puts it in a parser list. It also returns it to your code, which may in some advanced uses be handy. parse_args() returns a argparse.Namespace object.
You can subclass the argparse classes to customize their performance.
But usually you don't need to create your own parser class - unless you need a lot of special behavior.
Here's what I'd consider to be a clean use of argparse:
import argparse
# your code
def main(args):
foo = args.foo
# other uses of args
def other(foo, bar):
# code using values
def make_parser():
parser = argparse.ArgumentParser(description='script 1.0')
parser.add_argument('-a', '--foo', help='specify foo')
parser.add_argument('-b', '--bar', type=int, help='specify bar')
parser.add_argument('-c', '--baz', help='specify baz')
parser.add_argument('-d', '--bar2', help='bar2')
return parser
if __name__ == '__main__':
parser = make_parser()
args = parser.parse_args()
Wrapping the parser definition in a function is a good idea. In more complex cases it could be a class. But normally you only need one parser per script, so a function is enough. That function can be in the main body of the script, but actual use should be in a if __name__ block, so the script can be imported without using the parser.
This puts the args namespace in the global environment, where is accessible to all your code. It can be passed as to your functions, or selected values can be passed:
main(args)
other(args.foo, args.bar)
foo = args.foo
do_things3(foo)
d = vars(args) # args in dictionary form
It's a good idea to write your code so it works as imported classes and/or functions, and when run as a script. The argparse part sets values when run as a script. When imported, the importing script sets the necessary control values, possibly with its own parser.
I would do it this way:
import argparse
Class Program:
def __init__(self, foo, bar, baz, bar2):
self.foo = foo
self.bar = bar
...(so on for each arg)
def do_things():
pass
def get_args():
parser = argparse.ArgumentParser(description='script 1.0')
parser.add_argument('-a', '--foo', help='specify foo')
parser.add_argument('-b', '--bar', type=int, help='specify bar')
parser.add_argument('-c', '--baz', help='specify baz')
parser.add_argument('-d', '--bar2', help='bar2')
return parser.parse_args()
def main(args):
instance = Program(args.foo,
args.bar,
args.baz,
args.bar2)
instance.do_things()
if __name__ == '__main__':
args = get_args()
main(args)
Explanation:
Try to separate things...
Parsing the arguments is done in the get_args function, and then pass the "args" to the main.
The main function is in charge to initialize the object by the parameters that been passed as args and perform some work on the object.

How to add description to Django command?

In argparse, the description is passed into the ArgumentParser constructor like so:
import argparse
parser = argparse.ArgumentParser(description="Print a number")
parser.add_argument('-n', dest='number', action='store', type=str,
help="The number to be printed")
args = parser.parse_args()
When doing a similar thing using a Django command, how does one add the description="Print a number" given that the ArgumentParser has already been constructed?
from django.core.management.base import BaseCommand
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument('-n', dest='number', action='store', type=str,
help="The number to be printed")
Set its help attribute:
BaseCommand.help
A short description of the command, which will be printed in the help message when the user runs the command python manage.py help <command>.
For example:
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Command description'
def add_arguments(self, parser):
# ...

Categories

Resources