Skip to content

Commit b61b93a

Browse files
Logging: handle verbosity as a ResourceOption (#12)
* fix(logging): handle verbosity as a ResourceOption When `verbosity_option` is a `ResourceOption` (to make the verbosity settable by a config), the logging level will not be set for the logs in the config files. * test(logging): handle all `verbosity_option` cases Ensure the log level is set in config files when `verbosity_option` is not a `ResourceOption`. Ensure the log level is still correctly imported from the config when `verbosity_option` is a `ResourceOption`. * docs: add details on verbosity option as Resource. Add information on the behavior of the `verbosity_option` when it is set as `ResourceOption`. Fix a typos in an help message (propagating the change in the tests).
1 parent 92c483e commit b61b93a

File tree

4 files changed

+191
-16
lines changed

4 files changed

+191
-16
lines changed

src/clapper/click.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ def verbosity_option(
5858
* 2 (``-vv``): ``logger.setLevel(logging.INFO)``
5959
* 3 (``-vvv`` or more): ``logger.setLevel(logging.DEBUG)``
6060
61+
The verbosity level specified in this option will also be set during the loading of
62+
the ``ResourceOption`` or ``CommandConfig`` configuration files **unless**
63+
``verbosity_option`` is a ``ResourceOption`` itself. If this is the case, the
64+
logging level during the configuration loading will be the default level of the
65+
logger and the option will only effect the logging after the options handling.
66+
6167
6268
Arguments:
6369
@@ -68,7 +74,7 @@ def verbosity_option(
6874
name: Long name of the option. If not set, then use ``verbose`` --
6975
this will also become the name of the contextual parameter for click.
7076
71-
dlft: The default verbosity level to use (defaults to 0).
77+
dflt: The default verbosity level to use (defaults to 0).
7278
7379
**kwargs: Further keyword-arguments to be forwarded to the underlying
7480
:py:func:`click.option`
@@ -102,14 +108,14 @@ def callback(ctx, param, value):
102108
default=dflt,
103109
show_default=True,
104110
help=(
105-
f"Increase the verbosity level from 0 (only error and "
106-
f"critical) messages will be displayed, to 1 (like 0, but adds "
107-
f"warnings), 2 (like 1, but adds info messags), and 3 (like 2, "
108-
f"but also adds debugging messages) by adding the --{name} "
109-
f"option as often as desired (e.g. '-vvv' for debug)."
111+
f"Increase the verbosity level from 0 (only error and critical) "
112+
f"messages will be displayed, to 1 (like 0, but adds warnings), 2 "
113+
f"(like 1, but adds info messages), and 3 (like 2, but also adds "
114+
f"debugging messages) by adding the --{name} option as often as "
115+
f"desired (e.g. '-vvv' for debug)."
110116
),
111117
callback=callback,
112-
is_eager=True, # Ensure the logger is set for ResourceOptions loading
118+
is_eager=kwargs.get("cls", None) is not ResourceOption,
113119
**kwargs,
114120
)(f)
115121

@@ -380,7 +386,7 @@ def consume_value(
380386
) -> tuple[typing.Any, ParameterSource]:
381387
"""Retrieve value for parameter from appropriate context.
382388
383-
This method will retrive the value of its own parameter from the
389+
This method will retrieve the value of its own parameter from the
384390
appropriate context, by trying various sources.
385391
386392
Parameters
@@ -645,7 +651,7 @@ def config_group(
645651
"""Add a command group to list/describe/copy job configurations.
646652
647653
This decorator adds a whole command group to a user predefined function
648-
which is part of the user's CLI. The command group provdes an interface to
654+
which is part of the user's CLI. The command group provides an interface to
649655
list, fully describe or locally copy configuration files distributed with
650656
the package. Commands accept both entry-point or module names to be
651657
provided as input.

tests/data/test_dump_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313

1414
# verbose = 0
1515
"""Optional parameter: verbose (-v, --verbose) [default: 0]
16-
Increase the verbosity level from 0 (only error and critical) messages will be displayed, to 1 (like 0, but adds warnings), 2 (like 1, but adds info messags), and 3 (like 2, but also adds debugging messages) by adding the --verbose option as often as desired (e.g. '-vvv' for debug)."""
16+
Increase the verbosity level from 0 (only error and critical) messages will be displayed, to 1 (like 0, but adds warnings), 2 (like 1, but adds info messages), and 3 (like 2, but also adds debugging messages) by adding the --verbose option as often as desired (e.g. '-vvv' for debug)."""

tests/data/test_dump_config2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,4 @@
4242

4343
# verbose = 0
4444
"""Optional parameter: verbose (-v, --verbose) [default: 0]
45-
Increase the verbosity level from 0 (only error and critical) messages will be displayed, to 1 (like 0, but adds warnings), 2 (like 1, but adds info messags), and 3 (like 2, but also adds debugging messages) by adding the --verbose option as often as desired (e.g. '-vvv' for debug)."""
45+
Increase the verbosity level from 0 (only error and critical) messages will be displayed, to 1 (like 0, but adds warnings), 2 (like 1, but adds info messages), and 3 (like 2, but also adds debugging messages) by adding the --verbose option as often as desired (e.g. '-vvv' for debug)."""

tests/test_logging.py

Lines changed: 174 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,10 @@ def cli_config():
243243
@verbosity_option(logger, expose_value=False)
244244
def cli(**_):
245245
"""This is the documentation provided by the user."""
246-
pass
246+
logger.debug("App Debug level message")
247+
logger.info("App Info level message")
248+
logger.warning("App Warning level message")
249+
logger.error("App Error level message")
247250

248251
return (cli, messages)
249252

@@ -255,7 +258,7 @@ def test_logger_click_option_config_q(cli_config):
255258
cli, log_output = cli_config
256259
runner = CliRunner()
257260
result = runner.invoke(cli, ["--cmp", "complex-var"])
258-
expected = "[ERROR] Error level message\n"
261+
expected = "[ERROR] Error level message\n" "[ERROR] App Error level message\n"
259262
assert result.exit_code == 0, result.output
260263
assert log_output.getvalue() == expected
261264

@@ -264,7 +267,12 @@ def test_logger_click_option_config_v(cli_config):
264267
cli, log_output = cli_config
265268
runner = CliRunner()
266269
result = runner.invoke(cli, ["--cmp", "complex-var", "-v"])
267-
expected = "[WARNING] Warning level message\n" "[ERROR] Error level message\n"
270+
expected = (
271+
"[WARNING] Warning level message\n"
272+
"[ERROR] Error level message\n"
273+
"[WARNING] App Warning level message\n"
274+
"[ERROR] App Error level message\n"
275+
)
268276
assert result.exit_code == 0, result.output
269277
assert log_output.getvalue() == expected
270278

@@ -277,6 +285,9 @@ def test_logger_click_option_config_vv(cli_config):
277285
"[INFO] Info level message\n"
278286
"[WARNING] Warning level message\n"
279287
"[ERROR] Error level message\n"
288+
"[INFO] App Info level message\n"
289+
"[WARNING] App Warning level message\n"
290+
"[ERROR] App Error level message\n"
280291
)
281292
assert result.exit_code == 0, result.output
282293
assert log_output.getvalue() == expected
@@ -291,17 +302,23 @@ def test_logger_click_option_config_vvv(cli_config):
291302
"[INFO] Info level message\n"
292303
"[WARNING] Warning level message\n"
293304
"[ERROR] Error level message\n"
305+
"[DEBUG] App Debug level message\n"
306+
"[INFO] App Info level message\n"
307+
"[WARNING] App Warning level message\n"
308+
"[ERROR] App Error level message\n"
294309
)
295310
assert result.exit_code == 0, result.output
296311
assert log_output.getvalue().endswith(expected)
297312

298313

299314
# Loading configs using ConfigCommand
315+
316+
300317
def test_logger_click_command_config_q(cli_config):
301318
cli, log_output = cli_config
302319
runner = CliRunner()
303320
result = runner.invoke(cli, ["complex"])
304-
expected = "[ERROR] Error level message\n"
321+
expected = "[ERROR] Error level message\n" "[ERROR] App Error level message\n"
305322
assert result.exit_code == 0, result.output
306323
assert log_output.getvalue() == expected
307324

@@ -310,7 +327,12 @@ def test_logger_click_command_config_v(cli_config):
310327
cli, log_output = cli_config
311328
runner = CliRunner()
312329
result = runner.invoke(cli, ["complex", "-v"])
313-
expected = "[WARNING] Warning level message\n" "[ERROR] Error level message\n"
330+
expected = (
331+
"[WARNING] Warning level message\n"
332+
"[ERROR] Error level message\n"
333+
"[WARNING] App Warning level message\n"
334+
"[ERROR] App Error level message\n"
335+
)
314336
assert result.exit_code == 0, result.output
315337
assert log_output.getvalue() == expected
316338

@@ -323,6 +345,9 @@ def test_logger_click_command_config_vv(cli_config):
323345
"[INFO] Info level message\n"
324346
"[WARNING] Warning level message\n"
325347
"[ERROR] Error level message\n"
348+
"[INFO] App Info level message\n"
349+
"[WARNING] App Warning level message\n"
350+
"[ERROR] App Error level message\n"
326351
)
327352
assert result.exit_code == 0, result.output
328353
assert log_output.getvalue() == expected
@@ -337,6 +362,150 @@ def test_logger_click_command_config_vvv(cli_config):
337362
"[INFO] Info level message\n"
338363
"[WARNING] Warning level message\n"
339364
"[ERROR] Error level message\n"
365+
"[DEBUG] App Debug level message\n"
366+
"[INFO] App Info level message\n"
367+
"[WARNING] App Warning level message\n"
368+
"[ERROR] App Error level message\n"
340369
)
341370
assert result.exit_code == 0, result.output
342371
assert log_output.getvalue().endswith(expected)
372+
373+
374+
# Verbosity option set in config file is ignored (not a ResourceOption)
375+
376+
377+
def test_logger_click_command_config_q_plus_config(cli_config):
378+
cli, log_output = cli_config
379+
runner = CliRunner()
380+
result = runner.invoke(cli, ["verbose-config", "complex"])
381+
expected = "[ERROR] Error level message\n" "[ERROR] App Error level message\n"
382+
assert result.exit_code == 0, result.output
383+
assert log_output.getvalue() == expected
384+
385+
386+
def test_logger_click_command_config_v_plus_config(cli_config):
387+
cli, log_output = cli_config
388+
runner = CliRunner()
389+
result = runner.invoke(cli, ["verbose-config", "complex", "-v"])
390+
expected = (
391+
"[WARNING] Warning level message\n"
392+
"[ERROR] Error level message\n"
393+
"[WARNING] App Warning level message\n"
394+
"[ERROR] App Error level message\n"
395+
)
396+
assert result.exit_code == 0, result.output
397+
assert log_output.getvalue() == expected
398+
399+
400+
# Testing verbosity option as config option (legacy mode)
401+
# The logging level will not be correct in the config files, but can be set as a config.
402+
403+
404+
@pytest.fixture
405+
def cli_verbosity_config():
406+
messages = io.StringIO()
407+
logger = clapper.logging.setup(
408+
"clapper_test",
409+
format="[%(levelname)s] %(message)s",
410+
low_level_stream=messages,
411+
high_level_stream=messages,
412+
)
413+
logger.setLevel("ERROR") # Enforce a default level
414+
415+
@click.command(entry_point_group="clapper.test.config", cls=ConfigCommand)
416+
@click.option("--cmp", entry_point_group="clapper.test.config", cls=ResourceOption)
417+
@verbosity_option(logger, expose_value=False, cls=ResourceOption)
418+
def cli(**_):
419+
"""This is the documentation provided by the user."""
420+
logger.debug("App Debug level message")
421+
logger.info("App Info level message")
422+
logger.warning("App Warning level message")
423+
logger.error("App Error level message")
424+
425+
return (cli, messages)
426+
427+
428+
def test_logger_click_option_config_verbose_as_config_q(cli_verbosity_config):
429+
cli, log_output = cli_verbosity_config
430+
runner = CliRunner()
431+
result = runner.invoke(cli, ["--cmp", "complex-var"])
432+
expected = "[ERROR] Error level message\n" "[ERROR] App Error level message\n"
433+
assert result.exit_code == 0, result.output
434+
assert log_output.getvalue() == expected
435+
436+
437+
def test_logger_click_option_config_verbose_as_config_v(cli_verbosity_config):
438+
cli, log_output = cli_verbosity_config
439+
runner = CliRunner()
440+
result = runner.invoke(cli, ["--cmp", "complex-var", "-v"])
441+
expected = (
442+
"[ERROR] Error level message\n"
443+
"[WARNING] App Warning level message\n"
444+
"[ERROR] App Error level message\n"
445+
)
446+
assert result.exit_code == 0, result.output
447+
assert log_output.getvalue() == expected
448+
449+
450+
def test_logger_click_option_config_verbose_as_config_vv(cli_verbosity_config):
451+
cli, log_output = cli_verbosity_config
452+
runner = CliRunner()
453+
result = runner.invoke(cli, ["--cmp", "complex-var", "-vv"])
454+
expected = (
455+
"[ERROR] Error level message\n"
456+
"[INFO] App Info level message\n"
457+
"[WARNING] App Warning level message\n"
458+
"[ERROR] App Error level message\n"
459+
)
460+
assert result.exit_code == 0, result.output
461+
assert log_output.getvalue() == expected
462+
463+
464+
def test_logger_click_option_config_verbose_as_config_vvv(cli_verbosity_config):
465+
cli, log_output = cli_verbosity_config
466+
runner = CliRunner()
467+
result = runner.invoke(cli, ["--cmp", "complex-var", "-vvv"])
468+
expected_start = "[ERROR] Error level message\n"
469+
expected_end = (
470+
"[DEBUG] App Debug level message\n"
471+
"[INFO] App Info level message\n"
472+
"[WARNING] App Warning level message\n"
473+
"[ERROR] App Error level message\n"
474+
)
475+
assert result.exit_code == 0, result.output
476+
output = log_output.getvalue()
477+
assert output.startswith(expected_start)
478+
assert output.endswith(expected_end)
479+
480+
481+
# Verbosity option set in config file is handled (verbosity_option is a ResourceOption)
482+
483+
484+
def test_logger_option_config_verbose_as_config_q_plus_config(cli_verbosity_config):
485+
cli, log_output = cli_verbosity_config
486+
runner = CliRunner()
487+
result = runner.invoke(cli, ["verbose-config", "complex"])
488+
expected = (
489+
"[ERROR] Error level message\n"
490+
"[INFO] App Info level message\n"
491+
"[WARNING] App Warning level message\n"
492+
"[ERROR] App Error level message\n"
493+
)
494+
assert result.exit_code == 0, result.output
495+
assert log_output.getvalue() == expected
496+
497+
498+
# specifying the verbosity option in the CLI overrides the option
499+
500+
501+
def test_logger_option_config_verbose_as_config_v_plus_config(cli_verbosity_config):
502+
cli, log_output = cli_verbosity_config
503+
runner = CliRunner()
504+
result = runner.invoke(cli, ["verbose-config", "complex", "-v"])
505+
expected = (
506+
"[ERROR] Error level message\n"
507+
"[WARNING] App Warning level message\n"
508+
"[ERROR] App Error level message\n"
509+
)
510+
assert result.exit_code == 0, result.output
511+
assert log_output.getvalue() == expected

0 commit comments

Comments
 (0)