Last active
December 1, 2021 17:14
-
-
Save elmotec/6008118 to your computer and use it in GitHub Desktop.
Minimal python program with logging and argparse.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!python | |
"""See main.__doc__""" | |
import os | |
import logging | |
import glob | |
import unittest | |
import sys | |
import click | |
import click.testing | |
module = sys.modules["__main__"].__file__ | |
log = logging.getLogger(module) | |
def process(filename, output): | |
"""Do something more useful that printing out the file name""" | |
print(filename) | |
@click.command() | |
@click.argument("files", nargs=-1) | |
@click.option( | |
"--output", | |
help="output directory where to write to", | |
default=os.getenv("TEMP"), | |
type=click.Path(), | |
) | |
@click.option("--verbose", "-v", help="increases verbosity", count=True) | |
def main(files, output, verbose): | |
"""This help line will show in the command line --help output""" | |
logging.root.setLevel(max(3 - verbose, 0) * 10) # impact all loggers | |
log.debug("log level: %s", log.getEffectiveLevel()) | |
log.debug("output: %s", output) | |
# credit https://stackoverflow.com/questions/48604754 | |
all_files = [] | |
for argument in files: | |
log.debug("processing argument %s ...", argument) | |
# if our shell does not do filename globbing | |
expanded = list(glob.glob(argument)) | |
if len(expanded) == 0 and "*" not in argument: | |
raise (click.BadParameter("{}: file not found".format(argument))) | |
all_files.extend(expanded) | |
# Actual processing of the files. | |
for filename in all_files: | |
log.info("processing file %s ...", filename) | |
process(filename, output) | |
if __name__ == "__main__": | |
logging.basicConfig( | |
stream=sys.stderr, | |
level=logging.DEBUG, | |
format="%(name)s (%(levelname)s): %(message)s", | |
) | |
try: | |
main() | |
except KeyboardInterrupt: | |
log.info("%s interrupted", module) | |
finally: | |
logging.shutdown() | |
class BasicCommandLineTest(unittest.TestCase): | |
"""Tests can be run with python -m unittest <script>""" | |
def setUp(self): | |
self.runner = click.testing.CliRunner() | |
def test_offers_help_for_invalid_option(self): | |
"""Shows usage when the option is not valid""" | |
result = self.runner.invoke(main, ["--invalid"]) | |
self.assertEqual(result.exit_code, 2) | |
self.assertRegex(result.output, r"Try.*--help") | |
def test_shows_help(self): | |
"""Makes sure the help is available.""" | |
result = self.runner.invoke(main, ["--help"]) | |
self.assertEqual(result.exit_code, 0) | |
self.assertRegex(result.output, r"Usage: ") | |
def test_one_v_for_info_level_logging(self): | |
"""-v sets logging to info.""" | |
_ = self.runner.invoke(main, ["-v"]) | |
self.assertEqual(log.getEffectiveLevel(), logging.INFO) | |
def test_two_v_for_info_level_logging(self): | |
"""-vv sets logging to debug.""" | |
_ = self.runner.invoke(main, ["-vv"]) | |
self.assertEqual(log.getEffectiveLevel(), logging.DEBUG) |
The log is going to stderr. Only the output goes to the file referenced in -o. However, I use click nowadays for command line utilities. Much easier. I will probably delete that gist.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This doesn't seem to actually write the logs to a file when
-o
is specified. It only creates the file.