Coverage for python/lsst/daf/butler/tests/cliLogTestBase.py : 36%

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# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <http://www.gnu.org/licenses/>.
22"""Unit tests for the daf_butler CliLog utility. Code is implemented in
23daf_butler but some only runs if lsst.log.Log can be imported so these parts of
24it can't be tested there because daf_butler does not directly depend on
25lsst.log, and only uses it if it has been setup by another package."""
27import click
28from collections import namedtuple
29from functools import partial
30import logging
31import unittest
33from lsst.daf.butler.cli.butler import cli as butlerCli
34from lsst.daf.butler.cli.cliLog import CliLog
35from lsst.daf.butler.cli.utils import clickResultMsg, command_test_env, LogCliRunner
36try:
37 import lsst.log as lsstLog
38except ModuleNotFoundError:
39 lsstLog = None
42def hasLsstLogHandler(logger):
43 """Check if a python logger has an lsst.log.LogHandler installed.
45 Parameters
46 ----------
47 logger : `logging.logger`
48 A python logger.
50 Returns
51 ------
52 `bool`
53 True if the logger has an lsst.log.LogHander installed, else False.
54 """
55 if lsstLog is None:
56 return False
57 for handler in logging.getLogger().handlers:
58 if isinstance(handler, lsstLog.LogHandler):
59 return True
62@click.command()
63@click.option("--expected-pyroot-level")
64@click.option("--expected-pybutler-level")
65@click.option("--expected-lsstroot-level")
66@click.option("--expected-lsstbutler-level")
67def command_log_settings_test(expected_pyroot_level,
68 expected_pybutler_level,
69 expected_lsstroot_level,
70 expected_lsstbutler_level):
71 if lsstLog is not None and not hasLsstLogHandler(logging.getLogger()):
72 raise click.ClickException("Expected to find an lsst.log handler in the python root logger's "
73 "handlers.")
75 LogLevel = namedtuple("LogLevel", ("expected", "actual", "name"))
77 logLevels = [LogLevel(expected_pyroot_level,
78 logging.getLogger().level,
79 "pyRoot"),
80 LogLevel(expected_pybutler_level,
81 logging.getLogger("lsst.daf.butler").level,
82 "pyButler")]
83 if lsstLog is not None:
84 logLevels.extend([LogLevel(expected_lsstroot_level,
85 lsstLog.getLogger("").getLevel(),
86 "lsstRoot"),
87 LogLevel(expected_lsstbutler_level,
88 lsstLog.getLogger("lsst.daf.butler").getLevel(),
89 "lsstButler")])
90 for expected, actual, name in logLevels:
91 if expected != actual:
92 raise(click.ClickException(f"expected {name} level to be {expected}, actual:{actual}"))
95class CliLogTestBase():
96 """Tests log initialization, reset, and setting log levels."""
98 lsstLogHandlerId = None
100 def setUp(self):
101 self.runner = LogCliRunner()
103 def tearDown(self):
104 self.lsstLogHandlerId = None
106 class PythonLogger:
107 """Keeps track of log level of a component and number of handlers
108 attached to it at the time this object was initialized."""
110 def __init__(self, component):
111 self.logger = logging.getLogger(component)
112 self.initialLevel = self.logger.level
114 class LsstLogger:
115 """Keeps track of log level for a component at the time this object was
116 initialized."""
117 def __init__(self, component):
118 self.logger = lsstLog.getLogger(component) if lsstLog else None
119 self.initialLevel = self.logger.getLevel() if lsstLog else None
121 def runTest(self, cmd):
122 """Test that the log context manager works with the butler cli to
123 initialize the logging system according to cli inputs for the duration
124 of the command execution and resets the logging system to its previous
125 state or expected state when command execution finishes."""
126 pyRoot = self.PythonLogger(None)
127 pyButler = self.PythonLogger("lsst.daf.butler")
128 lsstRoot = self.LsstLogger("")
129 lsstButler = self.LsstLogger("lsst.daf.butler")
131 with command_test_env(self.runner, "lsst.daf.butler.tests.cliLogTestBase",
132 "command-log-settings-test"):
133 result = cmd()
134 self.assertEqual(result.exit_code, 0, clickResultMsg(result))
136 if lsstLog is not None:
137 self.assertFalse(hasLsstLogHandler(logging.getLogger()),
138 msg="CliLog should remove the lsst.log handler it added to the root logger.")
139 self.assertEqual(pyRoot.logger.level, logging.INFO)
140 self.assertEqual(pyButler.logger.level, pyButler.initialLevel)
141 if lsstLog is not None:
142 self.assertEqual(lsstRoot.logger.getLevel(), lsstLog.INFO)
143 # lsstLogLevel can either be the inital level, or uninitialized or
144 # the defined default value.
145 expectedLsstLogLevel = ((lsstButler.initialLevel, ) if lsstButler.initialLevel != -1
146 else(-1, CliLog.defaultLsstLogLevel))
147 self.assertIn(lsstButler.logger.getLevel(), expectedLsstLogLevel)
149 def test_butlerCliLog(self):
150 """Test that the log context manager works with the butler cli to
151 initialize the logging system according to cli inputs for the duration
152 of the command execution and resets the logging system to its previous
153 state or expected state when command execution finishes."""
155 self.runTest(partial(self.runner.invoke,
156 butlerCli,
157 ["--log-level", "WARNING",
158 "--log-level", "lsst.daf.butler=DEBUG",
159 "command-log-settings-test",
160 "--expected-pyroot-level", logging.WARNING,
161 "--expected-pybutler-level", logging.DEBUG,
162 "--expected-lsstroot-level", lsstLog.WARN if lsstLog else 0,
163 "--expected-lsstbutler-level", lsstLog.DEBUG if lsstLog else 0]))
165 def test_helpLogReset(self):
166 """Verify that when a command does not execute, like when the help menu
167 is printed instead, that CliLog is still reset."""
169 self.runTest(partial(self.runner.invoke, butlerCli, ["command-log-settings-test", "--help"]))
172if __name__ == "__main__": 172 ↛ 173line 172 didn't jump to line 173, because the condition on line 172 was never true
173 unittest.main()