From 8bcc8f802431a89a8752f09d28ea114b88e3cbec Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 29 May 2015 11:33:11 -0700 Subject: [PATCH] Finished basic reporter --- letsencrypt/cli.py | 8 +-- letsencrypt/interfaces.py | 2 +- letsencrypt/{notifier.py => reporter.py} | 15 ++++- letsencrypt/tests/reporter_test.py | 73 ++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 7 deletions(-) rename letsencrypt/{notifier.py => reporter.py} (78%) create mode 100644 letsencrypt/tests/reporter_test.py diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index 0b8ef5761..85c2d2536 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -21,7 +21,7 @@ from letsencrypt import errors from letsencrypt import interfaces from letsencrypt import le_util from letsencrypt import log -from letsencrypt import notifier +from letsencrypt import reporter from letsencrypt.display import util as display_util from letsencrypt.display import ops as display_ops @@ -363,9 +363,9 @@ def main(args=sys.argv[1:]): zope.component.provideUtility(displayer) # Notifier - notify = notifier.Notifier() - zope.component.provideUtility(notify) - atexit.register(notify.print_messages) + report = reporter.Reporter() + zope.component.provideUtility(report) + atexit.register(report.print_messages) # Logging level = -args.verbose_count * 10 diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index 60cf358e5..1eea6b2d3 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -353,7 +353,7 @@ class IValidator(zope.interface.Interface): """Verify HSTS header is enabled.""" -class INotify(zope.interface.Interface): +class IReporter(zope.interface.Interface): """Interface to collect and display information to the user.""" HIGH_PRIORITY = zope.interface.Attribute( diff --git a/letsencrypt/notifier.py b/letsencrypt/reporter.py similarity index 78% rename from letsencrypt/notifier.py rename to letsencrypt/reporter.py index fa795673c..26269e3f8 100644 --- a/letsencrypt/notifier.py +++ b/letsencrypt/reporter.py @@ -9,16 +9,18 @@ import zope.interface from letsencrypt import interfaces -class Notifier(object): +class Reporter(object): """Collects and displays information to the user. :ivar `Queue.PriorityQueue` messages: Messages to be displayed to the user. """ - zope.interface.implements(interfaces.INotify) + zope.interface.implements(interfaces.IReporter) HIGH_PRIORITY, MEDIUM_PRIORITY, LOW_PRIORITY = xrange(3) + _RESET = '\033[0m' + _BOLD = '\033[1m' _msg_type = collections.namedtuple('Msg', 'priority, text, on_crash') def __init__(self): @@ -46,12 +48,21 @@ class Notifier(object): True are printed. """ + bold_on = False if not self.messages.empty(): no_exception = sys.exc_info()[0] is None + bold_on = sys.stdout.isatty() + if bold_on: + sys.stdout.write(self._BOLD) print 'IMPORTANT NOTES:' wrapper = textwrap.TextWrapper(initial_indent=' - ', subsequent_indent=' '*3) while not self.messages.empty(): msg = self.messages.get() if no_exception or msg.on_crash: + if bold_on and msg.priority > self.HIGH_PRIORITY: + sys.stdout.write(self._RESET) + bold_on = False print wrapper.fill(msg.text) + if bold_on: + sys.stdout.write(self._RESET) diff --git a/letsencrypt/tests/reporter_test.py b/letsencrypt/tests/reporter_test.py new file mode 100644 index 000000000..df9790256 --- /dev/null +++ b/letsencrypt/tests/reporter_test.py @@ -0,0 +1,73 @@ +"""Tests for letsencrypt/reporter.py""" +import StringIO +import sys +import unittest + + +class ReporterTest(unittest.TestCase): + def setUp(self): + from letsencrypt import reporter + self.reporter = reporter.Reporter() + + self.old_stdout = sys.stdout + sys.stdout = StringIO.StringIO() + + def tearDown(self): + sys.stdout = self.old_stdout + + def test_tty_print_empty(self): + sys.stdout.isatty = lambda: True + self.test_no_tty_print_empty() + + def test_no_tty_print_empty(self): + self.reporter.print_messages() + self.assertEqual(sys.stdout.getvalue(), "") + try: + raise ValueError + except ValueError: + self.reporter.print_messages() + self.assertEqual(sys.stdout.getvalue(), "") + + def test_tty_successful_exit(self): + sys.stdout.isatty = lambda: True + self._successful_exit_common() + + def test_no_tty_successful_exit(self): + self._successful_exit_common() + + def test_tty_unsuccessful_exit(self): + sys.stdout.isatty = lambda: True + self._unsuccessful_exit_common() + + def test_no_tty_unsuccessful_exit(self): + self._unsuccessful_exit_common() + + def _successful_exit_common(self): + self._add_messages() + self.reporter.print_messages() + output = sys.stdout.getvalue() + self.assertTrue("IMPORTANT NOTES:" in output) + self.assertTrue("High" in output) + self.assertTrue("Med" in output) + self.assertTrue("Low" in output) + + def _unsuccessful_exit_common(self): + self._add_messages() + try: + raise ValueError + except ValueError: + self.reporter.print_messages() + output = sys.stdout.getvalue() + self.assertTrue("IMPORTANT NOTES:" in output) + self.assertTrue("High" in output) + self.assertTrue("Med" not in output) + self.assertTrue("Low" not in output) + + def _add_messages(self): + self.reporter.add_message("High", self.reporter.HIGH_PRIORITY, True) + self.reporter.add_message("Med", self.reporter.MEDIUM_PRIORITY) + self.reporter.add_message("Low", self.reporter.LOW_PRIORITY) + + +if __name__ == "__main__": + unittest.main() # pragma: no cover