Call a module containing command line arguments from another module - python

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'])

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

How to test if __name__ == "__main__" with passing command line arguments?

Hi I want to test my executable module main.py.
In this module there is function main() that takes two arguments:
# main.py
def main(population_size: int, number_of_iterations: int):
...
At the bottom of this module there is logic that takes command line arguments and executes main function:
# main.py
if __name__ == "__main__":
# create parser and handle arguments
PARSER = argparse.ArgumentParser()
PARSER.add_argument("--populationSize",
type=int,
default=-1,
help="Number of individuals in one iteration")
PARSER.add_argument("--numberOfIterations",
type=int,
default=-1,
help="Number of iterations in one run")
# parse the arguments
ARGS = PARSER.parse_args()
main(ARGS.populationSize, ARGS.numberOfIterations)
I want to test passing command line arguments. My test method that doesn't work:
# test_main.py
#staticmethod
#mock.patch("argparse.ArgumentParser.parse_args")
#mock.patch("main.main")
def test_passing_arguments(mock_main, mock_argparse):
"""Test passing arguments."""
mock_argparse.return_value = argparse.Namespace(
populationSize=4, numberOfIterations=3)
imp.load_source("__main__", "main.py")
mock_main.assert_called_with(4, 3)
The error that I get is that mock_main is not called. I don't know why. To my understanding I mocked main function from main module. Mock of main function is neccessary becouse it's time consuming, and what I want to only test here is that parameters are passed correctly.
From this post I took way of mocking argparse module.
Like all code you want to test, wrap it in a function.
def parse_my_args(argv=None):
PARSER = argparse.ArgumentParser()
PARSER.add_argument("--populationSize",
type=int,
default=-1,
help="Number of individuals in one iteration")
PARSER.add_argument("--numberOfIterations",
type=int,
default=-1,
help="Number of iterations in one run")
# parse the arguments
return PARSER.parse_args(argv)
if __name__ == '__main__':
args = parse_my_args()
main(args.populationSize, args.numberOfIterations)
ArgumentParser.parse_args processes whatever list of strings you pass it. When you pass None, it uses sys.argv[1:] instead.
Now you can test parse_my_args simply by passing whatever list of arguments you want.
# test_main.py
#staticmethod
def test_passing_arguments():
"""Test passing arguments."""
args = parse_my_args(["--populationSize", "4", "--numberOfIterations", "3"])
assert args.populationSize == 4
assert args.numberOfIterations == 3
If you further want to verify that the correct arguments are passed to main, wrap that in a function and use mock as you did above.
def entry_point(argv=None):
args = parse_my_args(argv)
main(args.populationSize, args.numberOfIterations)
if __name__ == '__main__':
entry_point()
and
#staticmethod
#mock.patch("main.main")
def test_passing_arguments(mock_main):
"""Test passing arguments."""
entry_point(["--populationSize", "4", "--numberOfIterations", "3"])
mock_main.assert_called_with(4, 3)
I usually write my command-line code like this. First rename your existing main function to something else, like run() (or whatever):
def run(population_size: int, number_of_iterations: int):
...
Then write a main() function which implements the command-line interface and argument parsing. Have it accept argv as an optional argument which is great for testing:
def main(argv=None):
parser = argparse.ArgumentParser()
...
args = parser.parse_args(argv)
run(args.popuplation_size, args.number_of_iterations)
Then in the module body just put:
if __name__ == '__main__':
sys.exit(main())
Now you have a proper main() function that you can easily test without fussing about the context in which it was called or doing any sort of weird monkeypatching, e.g. like:
main(['--populationSize', '4', '--numberOfIterations', '3'])

Python: most elegant way of accessing __version__ inside main()

I am creating a small Python 3 program that takes command line arguments using argparse. This library has an automatic way of handling version flags. I am trying to figure out what is the most elegant way of accessing version inside my main() function.
#!/usr/bin/env python3
import argparse
def main():
__version__ = '0.1.0'
parser = argparse.ArgumentParser()
parser.add_argument('input',
type=str,
parser.add_argument('-v', '--version',
action='version',
version='%(prog)s ' + __version__
)
args = parser.parse_args()
# main code
print('foo bar')
if __name__ == '__main__':
main()
So what is the most elegant solution here?
1) do what I did above and just live with it.
2) leave __version__ outside main() and pass it as an argument to main() (though I always thought it was bad practise to use arguments in a main() function)
3) leave __version__ and all parser setup outsidemain()and pass the variableargs` to main() as an argument (bad practise again).
4) use global variables
Or am I mistaken that it is bad practise to send arguments to main()? Or alternatively, am I missing another solution altogether?
#!/usr/bin/env python3
import argparse
__version__ = '0.1.0'
def main():
# optionally put this argparse code in its own function
parser = argparse.ArgumentParser()
parser.add_argument('input',
type=str)
parser.add_argument('-v', '--version',
action='version',
version='%(prog)s ' + __version__
)
args = parser.parse_args()
# main code
print(args)
print('foo bar')
if __name__ == '__main__':
main()
testing
1136:~/mypy$ python3 stack57822847.py foobar
Namespace(input='foobar')
foo bar
1136:~/mypy$ python3 stack57822847.py -v
stack57822847.py 0.1.0

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.

Python: Pass arguments from argparse to imported scripts

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'

Categories

Resources