For a small module, I want to have a __main__.py file that is executed via Pythons -m argument. As per basic docs, this file looks like
import argparse
from . import MyClass
def getparser():
parser = argparse.ArgumentParser()
# […]
return parser
if __name__ == "__main__":
parser = getparser()
args = parser.parse_args()
c = MyClass()
# […]
I want to test this with runpy:
import runpy
def test_main():
runpy.run_module("mymodule")
# […]
which does not work because the __name__ in this case is not __main__. In principle one could omit the if __name__ == "__main__" condition to get this working.
But, I also want to document the routine with sphinxarg.ext. This requires to have the getparser() function available from sphinx. Removing the if __name__ == "__main__" condition then also runs the module within sphinxdoc, which is not what is wanted.
.. argparse::
:module: mymodule.__main__
:func: getparser
:prog: myprog
How can I structure this so that all use cases work well and the code is well-readable (i.e. the ``getparser()` function and the main code should not be distributed over different files)?
One method is to keep your
__main__.pyvery thin and import what you need and run it.So, you might extract your logic into another module, say
cli.py, and write amainfunction that functions the same as your code block you have underif __name__ == '__main__':.Then in
__main__.pyyou can simply have something like this, which will work withrunpy(although this is maybe unnecessary now!) and doesn't need sphinx or test modules to import it:This way, you're still able to import everything from
cli.py(or wherever you choose), test it, auto-document it, or whatever. With this approach you can also test yourmainfunction and not have the limitations that arise with trying to test usingrunpy.Extracting to a module like
cli.pyis technically optional. You could also keep all of this in__main__.py, but the main lesson is to minimize the amount of code underif __name__ == '__main__':. If I have this, it almost always looks something like this:You can also simply set
__name__with therun_namekeyword argument forrunpy.run_module:But I would suggest that extracting a testable
mainfunction is a better testing approach.