When creating a command line application (such as httpie
made by python) in Python
Writing ** entry_point ** in setup.py
is the mainstream, isn't it?
setup.py
from setuptools import setup
setup(
# ...Omission...
entry_points={
"console_scripts": [
"my-console-app=my_console_app.__main__:main"
]
},
# ...Omission...
)
In the above case, the main
function written in __main__.py
of the my_console_app
package is defined as an application called my-console-app
.
And the important thing is how to write this ** main function **. In this article, I will introduce the template of the main function that has become established while I was making console applications.
All you need for the main function
I think.
Command line applications typically take arguments. For example, in the case of httpie given as an example,
$ http PUT http://httpbin.org/put hello=world
Take an argument like this. (PUT
, http://httpbin.org/put
, hello = world
are all arguments)
There is a standard library called ** argparse ** to define and get command line arguments in Python.
from argparse import ArgumentParser
parser = ArgumentParser(
prog="my-console-app",
description="This is my console application.")
parser.add_argument(
"message",
action="store",
help="The message string.")
Define command line arguments like this. I will not write about argparse in detail because the article will be unnecessarily long. Please check by yourself.
The main function uses the parser created by this argpase to get command line arguments.
__main__.py
from .cli import parser #Cli inside the package.Define parser in py file
def main(argv=sys.argv):
args = parser.parse_args()
Next, you need to implement the application itself. This application uses the command line arguments obtained above to change the output content, so
__main__.py
from .cli import parser
from .core import program #Core inside the package.Implement the application in py
def main(argv=sys.argv):
args = parser.parse_args()
program(args) #Since command line arguments are required to execute the application, take args as an argument
It is good to take the command line argument after parsing as the argument of the program like this.
What is the exit status? The exit status (English: exit status) or return code (English: return code) of a process in computer programming is a specific procedure or task to which a child process (or called party) is delegated. Is a small number to pass to the parent process (or caller) when it completes execution. [Exit Status-Wikipedia](https://ja.wikipedia.org/wiki/%E7%B5%82%E4%BA%86%E3%82%B9%E3%83%86%E3%83%BC% E3% 82% BF% E3% 82% B9)
Find out more about exit status yourself. Anyway, when an application quits, it's an integer ** to determine if the application quits correctly or if it quits abnormally with an error. Let's embed this in the main function.
Regarding this exit status, it is better to implement it so that the program inside the application returns the exit status rather than defining it in the main function. What does that mean?
__main__.py
import sys
from .cli import parser
from .core import program
def main(argv=sys.argv):
args = parser.parse_args()
exit_status = program(args) #It is good to implement so that the program inside the application returns the exit status.
return sys.exit(exit_status)
This is what it is.
Since it is an application that runs on the console, it is more user-friendly to output errors properly.
$ my-console-app -m 1111
Traceback (most recent call last):
File "/home/vagrant/.anyenv/envs/pyenv/versions/2.7.5/lib/python2.7/runpy.py", line 162, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/home/vagrant/.anyenv/envs/pyenv/versions/2.7.5/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/home/vagrant/lab/Python/my-console-app/my_console_app/__main__.py", line 63, in <module>
main()
File "/home/vagrant/lab/Python/my-console-app/my_console_app/__main__.py", line 52, in main
exit_code = program(args)
File "/home/vagrant/lab/Python/my-console-app/my_console_app/__main__.py", line 34, in program
print prettyprint(result)
File "my_console_app/utils.py", line 50, in prettyprint
raise TypeError("Message must be string not integer")
TypeError: Message must be string not integer
** The error I want to convey is only the last sentence **, but when so much information comes out, users who do nothing will be scared. Therefore, this error format should be displayed only for the last sentence.
__main__.py
import sys
from .cli import parser
from .core import program
def main(argv=sys.argv):
args = parser.parse_args()
try:
exit_status = program(args) #It is good to implement so that the program inside the application returns the exit status.
return sys.exit(exit_status)
except Exception as e:
error_type = type(e).__name__ # 29.6.2 Addendum: You can get the error name with this
sys.stderr.write("{0}: {1}\n".format(error_type, e.message))
sys.exit(1) #If the exit status is an integer other than 0, it indicates an abnormal termination.
If you write code like this, the error will be output as follows.
$ my-console-app -m 1111
TYpeError: Message must be string not integer
It's easier to see! !!
But if you think that you can't see the details of the error inside the application. There is a workaround.
--stack-trace
argument when an error occurscli.py
parser.add_argument(
"--stack-trace",
dest="stacktrace",
action="store_true",
help="Display the stack trace when error occured.")
__main__.py
import sys
import traceback
from .cli import parser
from .core import program
def main(argv=sys.argv):
if len(argv) == 1:
# 2017.04.29 Correction
# len(argv) ==It was 1 instead of 0. Excuse me
parser.parse_args(["-h"])
args = parser.parse_args(argv)
try:
exit_code = program(args)
sys.exit(exit_code)
except Exception as e:
error_type = type(e).__name__ # 29.6.2 Addendum: You can get the error name with this
stack_trace = traceback.format_exc() #Save the stack trace when an error occurs
if args.stacktrace: # --stack-Output stack trace if there is a trace argument
print "{:=^30}".format(" STACK TRACE ")
print stack_trace.strip()
else:
sys.stderr.write(
"{0}: {1}\n".format(e_type, e.message))
sys.exit(1)
In other words, the template of the main function is as follows.
__main__.py
import sys
import traceback
from .cli import parser
from .core import program
def main(argv=sys.argv):
if len(argv) == 1:
# 2017.04.29 Correction
# len(argv) ==It was 1 instead of 0. Excuse me
parser.parse_args(["-h"]) #Output help message if there are no command line arguments
sys.exit(0)
args = parser.parse_args(argv)
try:
exit_code = program(args)
sys.exit(exit_code)
except Exception as e:
error_type = type(e).__name__ # 29.6.2 Addendum: You can get the error name with this
stack_trace = traceback.format_exc()
if args.stacktrace:
print "{:=^30}".format(" STACK TRACE ")
print stack_trace.strip()
else:
sys.stderr.write(
"{0}: {1}\n".format(e_type, e.message))
sys.exit(1)
Of the above code
if len(sys.argv) == 0:
parser.parse_args(["-h"])
There was an error in this part. Correctly
if len(sys.argv) == 1: # 0 -> 1
parser.parse_args(["-h"])
The purpose of this code (to get help message from parser) is that in the case of argparse, if you start the application without setting any command line arguments,
usage: setup-script [-h] [-l] [--stack-trace] formula project
setup-script: error: too few arguments
You will get an error message like this (meaning there are no arguments), so if sys.argv
is 1 with the intention of not giving this error,parser.parse_args (["-h"])
The help message is output as.
I learned that you can use type (e) .__ name__
to get the error name from the error object.
Recommended Posts