Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support setting output verbosity #889

Merged
merged 6 commits into from May 15, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.rst
Expand Up @@ -13,6 +13,7 @@ Changelog
- [watchmedo] Fix broken parsing of boolean arguments. (`#855 <https://github.com/gorakhargosh/watchdog/issues/855>`_)
- [watchmedo] Fix broken parsing of commands from ``auto-restart``, and ``shell-command``. (`#855 <https://github.com/gorakhargosh/watchdog/issues/855>`_)
- [inotify] Fix hang when unscheduling watch on a path in an unmounted filesystem. (`#869 <https://github.com/gorakhargosh/watchdog/pull/869>`_)
- [watchmedo] Support setting verbosity level via ``-q/--quiet`` and ``-v/--verbose`` arguments. (`#889 <https://github.com/gorakhargosh/watchdog/pull/889>`_)
- Thanks to our beloved contributors: @taleinat, @kianmeng, @palfrey, @IlayRosenberg, @BoboTiG

2.1.7
Expand Down
16 changes: 11 additions & 5 deletions src/watchdog/tricks/__init__.py
Expand Up @@ -41,6 +41,8 @@

"""

import functools
import logging
import os
import signal
import subprocess
Expand All @@ -50,6 +52,10 @@
from watchdog.events import PatternMatchingEventHandler


logger = logging.getLogger(__name__)
echo_events = functools.partial(echo.echo, write=lambda msg: logger.info(msg))


class Trick(PatternMatchingEventHandler):

"""Your tricks should subclass this class."""
Expand Down Expand Up @@ -80,19 +86,19 @@ class LoggerTrick(Trick):
def on_any_event(self, event):
pass

@echo.echo
@echo_events
def on_modified(self, event):
pass

@echo.echo
@echo_events
def on_deleted(self, event):
pass

@echo.echo
@echo_events
def on_created(self, event):
pass

@echo.echo
@echo_events
def on_moved(self, event):
pass

Expand Down Expand Up @@ -202,7 +208,7 @@ def kill_process(stop_signal):
pass
self.process = None

@echo.echo
@echo_events
def on_any_event(self, event):
self.stop()
self.start()
37 changes: 32 additions & 5 deletions src/watchdog/watchmedo.py
Expand Up @@ -60,7 +60,8 @@ def _split_lines(self, text, width):
return text.splitlines()


epilog = '''Copyright 2011 Yesudeep Mangalapilly <yesudeep@gmail.com>.
epilog = '''\
Copyright 2011 Yesudeep Mangalapilly <yesudeep@gmail.com>.
Copyright 2012 Google, Inc & contributors.

Licensed under the terms of the Apache license, version 2.0. Please see
Expand All @@ -69,6 +70,7 @@ def _split_lines(self, text, width):
cli = ArgumentParser(epilog=epilog, formatter_class=HelpFormatter)
cli.add_argument('--version', action='version', version=VERSION_STRING)
subparsers = cli.add_subparsers(dest='top_command')
command_parsers = {}


def argument(*name_or_flags, **kwargs):
Expand All @@ -94,6 +96,11 @@ def decorator(func):
description=desc,
aliases=cmd_aliases,
formatter_class=HelpFormatter)
verbosity_group = parser.add_mutually_exclusive_group()
verbosity_group.add_argument('-q', '--quiet', dest='verbosity',
action='append_const', const=-1)
verbosity_group.add_argument('-v', '--verbose', dest='verbosity',
action='append_const', const=1)
for arg in args:
parser.add_argument(*arg[0], **arg[1])
parser.set_defaults(func=func)
Expand Down Expand Up @@ -397,7 +404,8 @@ def log(args):
from watchdog.tricks import LoggerTrick

if args.trace:
echo.echo_class(LoggerTrick)
class_module_logger = logging.getLogger(LoggerTrick.__module__)
echo.echo_class(LoggerTrick, write=lambda msg: class_module_logger.info(msg))

patterns, ignore_patterns =\
parse_patterns(args.patterns, args.ignore_patterns)
Expand Down Expand Up @@ -632,14 +640,33 @@ def handler_termination_signal(_signum, _frame):
handler.stop()


def _get_log_level_from_args(args):
verbosity = sum(args.verbosity or [])
if verbosity < -1:
raise Exception("-q/--quiet may be specified only once.")
if verbosity > 2:
raise Exception("-v/--verbose may be specified up to 2 times.")
return ['ERROR', 'WARNING', 'INFO', 'DEBUG'][1 + verbosity]


def main():
"""Entry-point function."""
args = cli.parse_args()
if args.top_command is None:
cli.print_help()
else:
args.func(args)
return 1

try:
log_level = _get_log_level_from_args(args)
except Exception as exc:
print("Error: " + exc.args[0], file=sys.stderr)
command_parsers[args.top_command].print_help()
BoboTiG marked this conversation as resolved.
Show resolved Hide resolved
return 1
logging.getLogger('watchdog').setLevel(log_level)

args.func(args)
return 0


if __name__ == '__main__':
main()
sys.exit(main())
46 changes: 46 additions & 0 deletions tests/test_0_watchmedo.py
Expand Up @@ -96,6 +96,52 @@ def test_shell_command_arg_parsing():
assert args.command == "'cmd'"


@pytest.mark.parametrize("cmdline", [
["auto-restart", "-d", ".", "cmd"],
["log", "."]
])
@pytest.mark.parametrize("verbosity", [
([], "WARNING"),
(["-q"], "ERROR"),
(["--quiet"], "ERROR"),
(["-v"], "INFO"),
(["--verbose"], "INFO"),
(["-vv"], "DEBUG"),
(["-v", "-v"], "DEBUG"),
(["--verbose", "-v"], "DEBUG"),
])
def test_valid_verbosity(cmdline, verbosity):
(verbosity_cmdline_args, expected_log_level) = verbosity
cmd = [cmdline[0], *verbosity_cmdline_args, *cmdline[1:]]
args = watchmedo.cli.parse_args(cmd)
log_level = watchmedo._get_log_level_from_args(args)
assert log_level == expected_log_level


@pytest.mark.parametrize("cmdline", [
["auto-restart", "-d", ".", "cmd"],
["log", "."]
])
@pytest.mark.parametrize("verbosity_cmdline_args", [
["-q", "-v"],
["-v", "-q"],
["-qq"],
["-q", "-q"],
["--quiet", "--quiet"],
["--quiet", "-q"],
["-vvv"],
["-vvvv"],
["-v", "-v", "-v"],
["-vv", "-v"],
["--verbose", "-vv"],
])
def test_invalid_verbosity(cmdline, verbosity_cmdline_args):
cmd = [cmdline[0], *verbosity_cmdline_args, *cmdline[1:]]
with pytest.raises((Exception, SystemExit)):
args = watchmedo.cli.parse_args(cmd)
watchmedo._get_log_level_from_args(args)


@pytest.mark.parametrize("command", ["tricks-from", "tricks"])
def test_tricks_from_file(command, tmp_path):
tricks_file = tmp_path / "tricks.yaml"
Expand Down