Skip to content

Commit

Permalink
Allow print_level keyword argument in logger calls
Browse files Browse the repository at this point in the history
This simplifies using the logger functions.
  • Loading branch information
bryanwweber committed May 3, 2021
1 parent c7f080d commit f9d8d68
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 26 deletions.
6 changes: 3 additions & 3 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ from buildutils import *

if not COMMAND_LINE_TARGETS:
# Print usage help
logger.info(__doc__, extra={"print_level": False})
logger.info(__doc__, print_level=False)
sys.exit(0)

valid_commands = ("build", "clean", "install", "uninstall",
"help", "msi", "samples", "sphinx", "doxygen", "dump")

for command in COMMAND_LINE_TARGETS:
if command not in valid_commands and not command.startswith('test'):
logger.error(f"Unrecognized command line target: {command!r}")
logger.error("Unrecognized command line target: {!r}", command)
sys.exit(1)

if "clean" in COMMAND_LINE_TARGETS:
Expand Down Expand Up @@ -134,7 +134,7 @@ if "test-clean" in COMMAND_LINE_TARGETS:
# *** Set system-dependent defaults for some options ***
# ******************************************************

logger.info(f"SCons is using the following Python interpreter: {sys.executable}")
logger.info("SCons is using the following Python interpreter: {}", sys.executable)

opts = Variables('cantera.conf')

Expand Down
92 changes: 69 additions & 23 deletions site_scons/buildutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pathlib import Path
import logging
from typing import TYPE_CHECKING
import collections

try:
import numpy as np
Expand All @@ -33,16 +34,61 @@
TPathLike = TypeVar("TPathLike", Path, str)


class LevelAdapter(logging.LoggerAdapter):
"""This adapter processes the ``print_level`` keyword-argument to log functions.
In the default Logger functions, it is not possible to add extra keyword arguments
to modify behavior. This Adapter allows the Logger functions (.debug(), .info(),
etc.) to include the keyword argument ``print_level``, which takes a Boolean value
to determine whether or not the level of the message should be shown with the rest
of the message. The actual message formatting is handled elsewhere. Example::
>>> logger.info("Message", print_level=True)
INFO: Message
>>> logger.error("Message", print_level=False)
Message
"""
def __init__(self, logger):
self.logger = logger

def process(self, msg, kwargs):
"""Pop the value of ``print_level`` into the ``extra`` dictionary.
Key-value pairs in the "extra" dictionary are set as attributes on the
``LogRecord`` instance.
"""
if "print_level" in kwargs:
print_level = kwargs.pop("print_level")
if "extra" in kwargs:
kwargs["extra"].update(print_level=print_level)
else:
kwargs["extra"] = {"print_level": print_level}
return msg, kwargs


# Modified from https://stackoverflow.com/a/42823461
class BraceLogRecord(logging.LogRecord):
"""Format a log record using brace syntax {} instead of %."""

def getMessage(self) -> str:
msg = str(self.msg)
if self.args:
if isinstance(self.args, collections.Mapping):
msg = msg.format_map(self.args)
else:
msg = msg.format(*self.args)
return msg


class OutputFormatter(logging.Formatter):
"""Format log output depending on whether the level should be shown.
When calling one of the logger functions (.debug(), .info(), etc.), the
``extra`` keyword argument can be passed a dictionary that sets attributes
on the ``LogRecord`` instance. If the dictionary has a ``print_level`` key
with a Boolean value, that will be used to determine whether the log level
should be included in the log message. Example::
Intended to be used with the LevelAdapter class to allow the ``print_level``
keyword argument to be added to Logger method calls (``.info()`` and others). The
``print_level`` Boolean value is used to determine whether or not the level of the
logging message should be printed. By default, the level is shown. Example::
>>> logger.info("Message", extra={"print_level": False})
>>> logger.info("Message", print_level=False)
Message
>>> logger.info("Message")
INFO: Message
Expand Down Expand Up @@ -89,6 +135,7 @@ def filter(self, record: logging.LogRecord) -> bool:
return False


logging.setLogRecordFactory(BraceLogRecord)
logger = logging.getLogger("cantera")
logger.setLevel(logging.INFO)
f = OutputFormatter()
Expand All @@ -102,6 +149,8 @@ def filter(self, record: logging.LogRecord) -> bool:
stderr_handler.addFilter(LevelFilter(logging.ERROR, logging.CRITICAL + 1))
logger.addHandler(stderr_handler)

logger = LevelAdapter(logger)


if sys.version_info[:2] < (3, 6):
logger.error(
Expand Down Expand Up @@ -216,11 +265,10 @@ def print_report(self, target, source, env):
"\n")
message = message + "*****************************"
if self.failed:
logger.error("One or more tests failed.\n" + message,
extra={"print_level": False})
logger.error("One or more tests failed.\n" + message, print_level=False)
sys.exit(1)
else:
logger.info(message, extra={"print_level": False})
logger.info(message, print_level=False)


test_results = TestResults()
Expand Down Expand Up @@ -269,7 +317,7 @@ def regression_test(target: "LFSNode", source: "LFSNode", env: "SCEnvironment"):
cwd=dir, env=env["ENV"], universal_newlines=True,
)
if ret.returncode:
logger.error(f"FAILED (program exit code:{ret.returncode})")
logger.error("FAILED (program exit code:{})", ret.returncode)
dir.joinpath(output_name).write_text(ret.stdout)

diff = 0
Expand All @@ -279,19 +327,17 @@ def regression_test(target: "LFSNode", source: "LFSNode", env: "SCEnvironment"):
comparisons.append((Path(blessed_name), output_name))

for blessed, output in comparisons:
logger.info(f"Comparing '{blessed}' with '{output}'",
extra={"print_level": False})
logger.info(f"Comparing '{blessed}' with '{output}'", print_level=False)
d = compare_files(env, dir.joinpath(blessed), dir.joinpath(output))
if d:
logger.error("FAILED", extra={"print_level": False})
logger.error("FAILED", print_level=False)
diff |= d

for blessed, output in env["test_profiles"]:
logger.info(f"Comparing '{blessed}' with '{output}'",
extra={"print_level": False})
logger.info(f"Comparing '{blessed}' with '{output}'", print_level=False)
d = compare_profiles(env, dir.joinpath(blessed), dir.joinpath(output))
if d:
logger.error("FAILED", extra={"print_level": False})
logger.error("FAILED", print_level=False)
diff |= d

del test_results.tests[env["active_test_name"]]
Expand All @@ -305,7 +351,7 @@ def regression_test(target: "LFSNode", source: "LFSNode", env: "SCEnvironment"):
if env["fast_fail_tests"]:
sys.exit(1)
else:
logger.info("PASSED", extra={"print_level": False})
logger.info("PASSED", print_level=False)
passed_file.write_text(time.asctime())
test_results.passed[env["active_test_name"]] = 1

Expand Down Expand Up @@ -384,9 +430,9 @@ def compare_text_files(env: "SCEnvironment", file1: Path, file2: Path) -> TestRe
relerr = abserr / (0.5 * abs(num1 + num2) + atol)
if abserr > atol and relerr > rtol:
logger.error(
f"Values differ: {num1:14g} {num2:14g}; "
f"rel. err = {relerr:.3e}; abs. err = {abserr:.3e}",
extra={"print_level": False},
"Values differ: {:14g} {:14g}; "
"rel. err = {:.3e}; abs. err = {:.3e}",
num1, num2, relerr, abserr, print_level=False,
)
all_match = False
break
Expand All @@ -402,7 +448,7 @@ def compare_text_files(env: "SCEnvironment", file1: Path, file2: Path) -> TestRe
message = [f"Found differences between {file1!s} and {file2!s}:", ">>>"]
message.extend(diff)
message.append("<<<")
logger.error("\n".join(message), extra={"print_level": False})
logger.error("\n".join(message), print_level=False)
return TestResult.FAIL

return TestResult.PASS
Expand Down Expand Up @@ -527,7 +573,7 @@ def compare_profiles(
if bad:
logger.error(
"\n".join(header + [template.format(*row) for row in bad] + footer),
extra={"print_level": False},
print_level=False,
)
return TestResult.FAIL
else:
Expand Down Expand Up @@ -731,7 +777,7 @@ def help(env: "SCEnvironment", options: "SCVariables") -> None:
lines.append(f" - actual: {actual!r}")
message.append("\n".join(lines))

logger.info("\n\n".join(message), extra={"print_level": False})
logger.info("\n\n".join(message), print_level=False)


def listify(value: "Union[str, Iterable]") -> "List[str]":
Expand Down

0 comments on commit f9d8d68

Please sign in to comment.