From 0e1f6b24f3a543140dc97928a50cdf69ffab61a7 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Wed, 27 May 2015 19:29:06 -0400 Subject: [PATCH] Added basic notifier --- letsencrypt/cli.py | 7 +++++ letsencrypt/interfaces.py | 27 +++++++++++++++++++ letsencrypt/notifier.py | 57 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 letsencrypt/notifier.py diff --git a/letsencrypt/cli.py b/letsencrypt/cli.py index defa7633d..8cb8ec84f 100644 --- a/letsencrypt/cli.py +++ b/letsencrypt/cli.py @@ -1,5 +1,6 @@ """Let's Encrypt CLI.""" # TODO: Sanity check all input. Be sure to avoid shell code etc... +import atexit import argparse import logging import os @@ -20,6 +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.display import util as display_util from letsencrypt.display import ops as display_ops @@ -347,6 +349,11 @@ def main(args=sys.argv[1:]): displayer = display_util.NcursesDisplay() zope.component.provideUtility(displayer) + # Notifier + notify = notifier.Notifier() + zope.component.provideUtility(notify) + atexit.register(notify.print_messages) + # Logging level = -args.verbose_count * 10 logger = logging.getLogger() diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index 609b9410a..b39737070 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -346,3 +346,30 @@ class IValidator(zope.interface.Interface): def hsts(name): """Verify HSTS header is enabled.""" + + +class INotify(zope.interface.Interface): + """Interface to collect and display information to the user.""" + + HIGH_PRIORITY = zope.interface.Attribute( + "Used to denote high priority messages") + MEDIUM_PRIORITY = zope.interface.Attribute( + "Used to denote medium priority messages") + LOW_PRIORITY = zope.interface.Attribute( + "Used to denote low priority messages") + + def add_message(self, msg, priority, on_crash=False): + """Adds msg to the list of messages to be printed. + + :param str msg: Message to be displayed to the user. + + :param int priority: One of HIGH_PRIORITY, MEDIUM_PRIORITY, or + LOW_PRIORITY. + + :param bool on_crash: Whether or not the message should be printed if + the program exits abnormally. + + """ + + def print_messages(self): + """Prints messages to the user and clears the message queue.""" diff --git a/letsencrypt/notifier.py b/letsencrypt/notifier.py new file mode 100644 index 000000000..fa795673c --- /dev/null +++ b/letsencrypt/notifier.py @@ -0,0 +1,57 @@ +"""Collects and displays information to the user.""" +import collections +import Queue +import sys +import textwrap + +import zope.interface + +from letsencrypt import interfaces + + +class Notifier(object): + """Collects and displays information to the user. + + :ivar `Queue.PriorityQueue` messages: Messages to be displayed to the user. + + """ + + zope.interface.implements(interfaces.INotify) + + HIGH_PRIORITY, MEDIUM_PRIORITY, LOW_PRIORITY = xrange(3) + _msg_type = collections.namedtuple('Msg', 'priority, text, on_crash') + + def __init__(self): + self.messages = Queue.PriorityQueue() + + def add_message(self, msg, priority, on_crash=False): + """Adds msg to the list of messages to be printed. + + :param str msg: Message to be displayed to the user. + + :param int priority: One of HIGH_PRIORITY, MEDIUM_PRIORITY, or + LOW_PRIORITY. + + :param bool on_crash: Whether or not the message should be printed if + the program exits abnormally. + + """ + assert priority >= self.HIGH_PRIORITY and priority <= self.LOW_PRIORITY + self.messages.put(self._msg_type(priority, msg, on_crash)) + + def print_messages(self): + """Prints messages to the user and clears the message queue. + + If there is an unhandled exception, only messages for which on_crash is + True are printed. + + """ + if not self.messages.empty(): + no_exception = sys.exc_info()[0] is None + 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: + print wrapper.fill(msg.text)