From 3a4a8113f80391efdd6a1c881a5cf97600a98b55 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sat, 9 Sep 2023 11:56:50 -0600 Subject: [PATCH 1/3] Add Preformatted class for logging preformatted messages --- pyomo/common/log.py | 18 +++++++++++- pyomo/common/tests/test_log.py | 53 ++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/pyomo/common/log.py b/pyomo/common/log.py index 3c4b01caf5a..bf2ae1e4c96 100644 --- a/pyomo/common/log.py +++ b/pyomo/common/log.py @@ -107,11 +107,14 @@ def __init__(self, **kwds): super(WrappingFormatter, self).__init__(**kwds) def format(self, record): + msg = record.getMessage() + if record.msg.__class__ is not str and isinstance(record.msg, Preformatted): + return msg + _orig = { k: getattr(record, k) for k in ('msg', 'args', 'pathname', 'levelname') } _id = getattr(record, 'id', None) - msg = record.getMessage() record.msg = self._flag record.args = None if _id: @@ -212,6 +215,19 @@ def emit(self, record): super(StdoutHandler, self).emit(record) +class Preformatted(object): + __slots__ = ('msg',) + + def __init__(self, msg): + self.msg = msg + + def __str__(self): + return str(self.msg) + + def __repr__(self): + return f'Preformatted({self.msg!r})' + + class _GlobalLogFilter(object): def __init__(self): self.logger = logging.getLogger() diff --git a/pyomo/common/tests/test_log.py b/pyomo/common/tests/test_log.py index 02afdd1dac1..8f3f7437727 100644 --- a/pyomo/common/tests/test_log.py +++ b/pyomo/common/tests/test_log.py @@ -25,11 +25,12 @@ import pyomo.common.unittest as unittest from pyomo.common.log import ( - LoggingIntercept, - WrappingFormatter, LegacyPyomoFormatter, + LoggingIntercept, LogHandler, LogStream, + Preformatted, + WrappingFormatter, pyomo_formatter, ) @@ -271,6 +272,54 @@ def test_numbered_level(self): ) self.assertEqual(self.stream.getvalue(), ans) + def test_preformatted(self): + self.handler.setFormatter( + LegacyPyomoFormatter( + base=os.path.dirname(__file__), + verbosity=lambda: logger.isEnabledFor(logging.DEBUG), + ) + ) + + msg = """This is a long multi-line message that in normal circumstances \ +would be line-wrapped + with additional information + that normally be combined.""" + + logger.setLevel(logging.WARNING) + logger.info(msg) + self.assertEqual(self.stream.getvalue(), "") + + logger.warning(Preformatted(msg)) + ans = msg + "\n" + self.assertEqual(self.stream.getvalue(), ans) + + logger.warning(msg) + ans += ( + "WARNING: This is a long multi-line message that in normal " + "circumstances would\n" + "be line-wrapped with additional information that normally be combined.\n" + ) + self.assertEqual(self.stream.getvalue(), ans) + + logger.setLevel(logging.DEBUG) + + logger.warning(Preformatted(msg)) + ans += msg + "\n" + self.assertEqual(self.stream.getvalue(), ans) + + logger.warning(msg) + lineno = getframeinfo(currentframe()).lineno - 1 + ans += 'WARNING: "[base]%stest_log.py", %d, test_preformatted\n' % ( + os.path.sep, + lineno, + ) + ans += ( + " This is a long multi-line message that in normal " + "circumstances would be\n" + " line-wrapped with additional information that normally be combined.\n" + ) + self.assertEqual(self.stream.getvalue(), ans) + def test_long_messages(self): self.handler.setFormatter( LegacyPyomoFormatter( From 1e4bb2f7b5b6fe1124ab5e5a0adcafcacb1615ad Mon Sep 17 00:00:00 2001 From: John Siirola Date: Mon, 18 Sep 2023 16:01:32 -0600 Subject: [PATCH 2/3] Add Preformatted() API tests --- pyomo/common/tests/test_log.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pyomo/common/tests/test_log.py b/pyomo/common/tests/test_log.py index 8f3f7437727..ad273addd6d 100644 --- a/pyomo/common/tests/test_log.py +++ b/pyomo/common/tests/test_log.py @@ -544,3 +544,18 @@ def test_log_stream(self): self.assertEqual(OUT.getvalue(), "INFO: line 1\n") # Exiting the context manager flushes the LogStream self.assertEqual(OUT.getvalue(), "INFO: line 1\nINFO: line 2\n") + + +class TestPreformatted(unittest.TestCase): + def test_preformatted_api(self): + ref = 'a message' + msg = Preformatted(ref) + self.assertIs(msg.msg, ref) + self.assertEqual(str(msg), ref) + self.assertEqual(repr(msg), "Preformatted('a message')") + + ref = 2 + msg = Preformatted(ref) + self.assertIs(msg.msg, ref) + self.assertEqual(str(msg), '2') + self.assertEqual(repr(msg), "Preformatted(2)") From 171cdf4cee27f584bbdd376fd0d9a1b26d5d70b7 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Tue, 19 Sep 2023 08:23:51 -0600 Subject: [PATCH 3/3] NFC: grammar update --- pyomo/common/tests/test_log.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyomo/common/tests/test_log.py b/pyomo/common/tests/test_log.py index ad273addd6d..39fab153e98 100644 --- a/pyomo/common/tests/test_log.py +++ b/pyomo/common/tests/test_log.py @@ -283,7 +283,7 @@ def test_preformatted(self): msg = """This is a long multi-line message that in normal circumstances \ would be line-wrapped with additional information - that normally be combined.""" + that normally would be combined.""" logger.setLevel(logging.WARNING) logger.info(msg) @@ -297,7 +297,7 @@ def test_preformatted(self): ans += ( "WARNING: This is a long multi-line message that in normal " "circumstances would\n" - "be line-wrapped with additional information that normally be combined.\n" + "be line-wrapped with additional information that normally would be combined.\n" ) self.assertEqual(self.stream.getvalue(), ans) @@ -316,7 +316,7 @@ def test_preformatted(self): ans += ( " This is a long multi-line message that in normal " "circumstances would be\n" - " line-wrapped with additional information that normally be combined.\n" + " line-wrapped with additional information that normally would be combined.\n" ) self.assertEqual(self.stream.getvalue(), ans)