mirror of
https://github.com/certbot/certbot.git
synced 2026-06-07 15:52:08 -04:00
Merge pull request #453 from bradmw/notify
Reporter (tells user about important stuff)
This commit is contained in:
commit
569a70f6aa
4 changed files with 175 additions and 0 deletions
|
|
@ -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 reporter
|
||||
|
||||
from letsencrypt.display import util as display_util
|
||||
from letsencrypt.display import ops as display_ops
|
||||
|
|
@ -359,6 +361,11 @@ def main(args=sys.argv[1:]):
|
|||
displayer = display_util.NcursesDisplay()
|
||||
zope.component.provideUtility(displayer)
|
||||
|
||||
# Reporter
|
||||
report = reporter.Reporter()
|
||||
zope.component.provideUtility(report)
|
||||
atexit.register(report.print_messages)
|
||||
|
||||
# Logging
|
||||
level = -args.verbose_count * 10
|
||||
logger = logging.getLogger()
|
||||
|
|
|
|||
|
|
@ -351,3 +351,30 @@ class IValidator(zope.interface.Interface):
|
|||
|
||||
def hsts(name):
|
||||
"""Verify HSTS header is enabled."""
|
||||
|
||||
|
||||
class IReporter(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."""
|
||||
|
|
|
|||
68
letsencrypt/reporter.py
Normal file
68
letsencrypt/reporter.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"""Collects and displays information to the user."""
|
||||
import collections
|
||||
import Queue
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import zope.interface
|
||||
|
||||
from letsencrypt import interfaces
|
||||
|
||||
|
||||
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.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):
|
||||
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.
|
||||
|
||||
"""
|
||||
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)
|
||||
73
letsencrypt/tests/reporter_test.py
Normal file
73
letsencrypt/tests/reporter_test.py
Normal file
|
|
@ -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
|
||||
Loading…
Reference in a new issue