Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""LICENSE
2Copyright 2019 Hermann Krumrey <hermann@krumreyh.com>
4This file is part of puffotter.
6puffotter is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
11puffotter is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
16You should have received a copy of the GNU General Public License
17along with puffotter. If not, see <http://www.gnu.org/licenses/>.
18LICENSE"""
20import os
21import sys
22import logging
23from logging.handlers import RotatingFileHandler
24from typing import Callable, Optional, Union, List
25from argparse import ArgumentParser, Namespace
26from puffotter.os import makedirs
29def cli_start(
30 main_func: Union[
31 Callable[[], None],
32 Callable[[Namespace], None],
33 Callable[[Namespace, logging.Logger], None]
34 ],
35 arg_parser: ArgumentParser,
36 exit_msg: str = "Goodbye",
37 package_name: Optional[str] = None,
38 sentry_dsn: Optional[str] = None,
39 release_name: Optional[str] = None
40):
41 """
42 Starts a program and sets up logging, as well as sentry error tracking
43 :param main_func: The main function to call
44 :param arg_parser: The argument parser to use
45 :param exit_msg: The message printed when the program's execution is
46 stopped using a keyboard interrupt
47 :param package_name: The package name of the application
48 :param sentry_dsn: The sentry DSN to use
49 :param release_name: The name of the release
50 :return: None
51 """
52 try:
53 args = arg_parser.parse_args()
54 setup_logging(args, package_name)
56 if release_name is None:
57 if package_name is not None:
58 import pkg_resources
59 version = pkg_resources.get_distribution(package_name).version
60 release_name = package_name + "-" + version
61 else:
62 release_name = "Unknown"
63 package_name = "unknown"
65 if sentry_dsn is not None:
66 import sentry_sdk
67 sentry_sdk.init(sentry_dsn, release=release_name)
69 from inspect import signature
70 sign = signature(main_func)
71 if len(sign.parameters) == 0:
72 main_func() # type: ignore
73 elif len(sign.parameters) == 1:
74 main_func(args) # type: ignore
75 elif len(sign.parameters) == 2:
76 logger = logging.getLogger(package_name)
77 main_func(args, logger) # type: ignore
78 else:
79 print("Invalid amount of parameters for main function")
80 except KeyboardInterrupt:
81 print(exit_msg)
84def argparse_add_verbosity(parser: ArgumentParser):
85 """
86 Adds --quiet, --verbose and --debug parameters to an ArgumentParser
87 :param parser: the parser to which to add those flags
88 :return: None
89 """
90 parser.add_argument("-q", "--quiet", action="store_true",
91 help="Sets the verbosity level of the program to "
92 "'quiet'")
93 parser.add_argument("-v", "--verbose", action="store_true",
94 help="Sets the verbosity level of the program to "
95 "'verbose'")
96 parser.add_argument("-d", "--debug", action="store_true",
97 help="Sets the verbosity level of the program to "
98 "'debug'")
99 parser.add_argument("--silent", action="store_true",
100 help="Silences all output to STDOUT")
103def argparse_add_logfile(parser: ArgumentParser):
104 """
105 Adds the --logfile argument to the argument parser
106 :param parser: The argument parser to modify
107 :return: None
108 """
109 parser.add_argument("--logfile",
110 help="Specifies the location of a logfile")
113def setup_logging(args: Namespace, package_name: Optional[str]):
114 """
115 Sets up logging for the provided arguments
116 :param args: The CLI arguments
117 :param package_name: The package name
118 :return: None
119 """
121 if "silent" in args and args.silent:
122 loglevel = 100 # Disable all logging output
123 sys.stdout = open(os.devnull, "w") # Disable all print output
124 elif "quiet" in args and args.quiet:
125 loglevel = logging.ERROR
126 elif "verbose" in args and args.verbose:
127 loglevel = logging.INFO
128 elif "debug" in args and args.debug:
129 loglevel = logging.DEBUG
130 else:
131 loglevel = logging.WARNING
133 file_formatter = logging.Formatter(
134 "[%(asctime)s] %(levelname)s:[%(filename)s:%(funcName)s(%(lineno)d)] "
135 "%(message)s",
136 datefmt="%Y-%m-%d:%H-%M-%S")
138 stream_formatter = logging.Formatter("%(levelname)s: %(message)s")
139 stream_handler = logging.StreamHandler()
140 stream_handler.setFormatter(stream_formatter)
141 stream_handler.setLevel(loglevel)
143 handlers = [stream_handler] # type: List[logging.Handler]
145 if "logfile" in args and args.logfile is not None:
146 log_file = args.logfile
147 file_handler = logging.FileHandler(log_file)
148 file_handler.setFormatter(file_formatter)
149 file_handler.setLevel(logging.DEBUG)
150 handlers.append(file_handler)
152 if package_name is not None:
153 log_dir = os.path.join(
154 os.path.expanduser("~"), ".config", package_name
155 )
156 makedirs(log_dir)
157 log_file = os.path.join(log_dir, "logs.log")
158 rotating_handler = RotatingFileHandler(
159 log_file, maxBytes=50000000, backupCount=10
160 )
161 rotating_handler.setFormatter(file_formatter)
162 rotating_handler.setLevel(logging.DEBUG)
163 handlers.append(rotating_handler)
165 logging.basicConfig(level=logging.DEBUG, handlers=handlers)