diff --git a/.travis.yml b/.travis.yml index 8101fb3a4..2c66f2a83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -103,7 +103,6 @@ addons: - python-dev - python-virtualenv - gcc - - dialog - libaugeas0 - libssl-dev - libffi-dev diff --git a/Vagrantfile b/Vagrantfile index e5975442f..23d3ddf13 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -29,6 +29,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # VM needs more memory to run test suite, got "OSError: [Errno 12] # Cannot allocate memory" when running # letsencrypt.client.tests.display.util_test.NcursesDisplayTest + # We may no longer need this. v.memory = 1024 # Handle cases when the host is behind a private network by making the diff --git a/certbot-apache/certbot_apache/display_ops.py b/certbot-apache/certbot_apache/display_ops.py index d7b76f83d..527de1001 100644 --- a/certbot-apache/certbot_apache/display_ops.py +++ b/certbot-apache/certbot_apache/display_ops.py @@ -100,5 +100,4 @@ def _vhost_menu(domain, vhosts): def _more_info_vhost(vhost): zope.component.getUtility(interfaces.IDisplay).notification( "Virtual Host Information:{0}{1}{0}{2}".format( - os.linesep, "-" * (display_util.WIDTH - 4), str(vhost)), - height=display_util.HEIGHT) + os.linesep, "-" * (display_util.WIDTH - 4), str(vhost))) diff --git a/certbot/cli.py b/certbot/cli.py index 83697d8da..8acb1f691 100644 --- a/certbot/cli.py +++ b/certbot/cli.py @@ -376,7 +376,7 @@ class HelpfulArgumentParser(object): # Do any post-parsing homework here - if self.verb == "renew" and not parsed_args.dialog_mode: + if self.verb == "renew": parsed_args.noninteractive_mode = True if parsed_args.staging or parsed_args.dry_run: @@ -388,17 +388,6 @@ class HelpfulArgumentParser(object): if parsed_args.must_staple: parsed_args.staple = True - # Avoid conflicting args - conficting_args = ["quiet", "noninteractive_mode", "text_mode"] - if parsed_args.dialog_mode: - for arg in conficting_args: - if getattr(parsed_args, arg): - raise errors.Error( - ("Conflicting values for displayer." - " {0} conflicts with dialog_mode").format(arg)) - elif parsed_args.verbose_count > flag_default("verbose_count"): - parsed_args.text_mode = True - if parsed_args.validate_hooks: hooks.validate_hooks(parsed_args) @@ -677,16 +666,13 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis "e.g. -vvv.") helpful.add( None, "-t", "--text", dest="text_mode", action="store_true", - help="Use the text output instead of the curses UI.") + help=argparse.SUPPRESS) helpful.add( [None, "automation"], "-n", "--non-interactive", "--noninteractive", dest="noninteractive_mode", action="store_true", help="Run without ever asking for user input. This may require " "additional command line flags; the client will try to explain " "which ones are required if it finds one missing") - helpful.add( - None, "--dialog", dest="dialog_mode", action="store_true", - help="Run using interactive dialog menus") helpful.add( [None, "run", "certonly"], "-d", "--domains", "--domain", dest="domains", @@ -890,6 +876,7 @@ def prepare_and_parse_args(plugins, args, detect_defaults=False): # pylint: dis " shell constructs, so you can use this switch to disable it.") helpful.add_deprecated_argument("--agree-dev-preview", 0) + helpful.add_deprecated_argument("--dialog", 0) _create_subparsers(helpful) _paths_parser(helpful) diff --git a/certbot/display/ops.py b/certbot/display/ops.py index 662483ee0..4339cedd6 100644 --- a/certbot/display/ops.py +++ b/certbot/display/ops.py @@ -219,7 +219,6 @@ def success_installation(domains): _gen_https_names(domains), os.linesep, os.linesep.join(_gen_ssl_lab_urls(domains))), - height=(10 + len(domains)), pause=False) @@ -241,7 +240,6 @@ def success_renewal(domains, action): os.linesep, os.linesep.join(_gen_ssl_lab_urls(domains)), action), - height=(14 + len(domains)), pause=False) diff --git a/certbot/display/util.py b/certbot/display/util.py index c0e9386cd..30d604313 100644 --- a/certbot/display/util.py +++ b/certbot/display/util.py @@ -1,9 +1,7 @@ """Certbot display.""" -import logging import os import textwrap -import dialog import six import zope.interface @@ -12,17 +10,7 @@ from certbot import errors from certbot.display import completer -logger = logging.getLogger(__name__) - WIDTH = 72 -HEIGHT = 20 - -DSELECT_HELP = ( - "Use the arrow keys or Tab to move between window elements. Space can be " - "used to complete the input path with the selected element in the " - "directory window. Pressing enter will select the currently highlighted " - "button.") -"""Help text on how to use dialog's dselect.""" # Display exit codes OK = "ok" @@ -59,170 +47,6 @@ def _wrap_lines(msg): return os.linesep.join(fixed_l) - -def _clean(dialog_result): - """Treat sundy python-dialog return codes as CANCEL - - :param tuple dialog_result: (code, result) - :returns: the argument but with unknown codes set to -1 (Error) - :rtype: tuple - """ - code, result = dialog_result - if code in (OK, HELP): - return dialog_result - elif code in (CANCEL, ESC): - return (CANCEL, result) - else: - logger.debug("Surprising dialog return code %s", code) - return (CANCEL, result) - - -@zope.interface.implementer(interfaces.IDisplay) -class NcursesDisplay(object): - """Ncurses-based display.""" - - def __init__(self, width=WIDTH, height=HEIGHT): - super(NcursesDisplay, self).__init__() - self.dialog = dialog.Dialog(autowidgetsize=True) - assert OK == self.dialog.DIALOG_OK, "What kind of absurdity is this?" - self.width = width - self.height = height - - def notification(self, message, height=10, pause=False): - # pylint: disable=unused-argument - """Display a notification to the user and wait for user acceptance. - - .. todo:: It probably makes sense to use one of the transient message - types for pause. It isn't straightforward how best to approach - the matter though given the context of our messages. - http://pythondialog.sourceforge.net/doc/widgets.html#displaying-transient-messages - - :param str message: Message to display - :param int height: Height of the dialog box - :param bool pause: Not applicable to NcursesDisplay - - """ - self.dialog.msgbox(message) - - def menu(self, message, choices, ok_label="OK", cancel_label="Cancel", - help_label="", **unused_kwargs): - """Display a menu. - - :param str message: title of menu - - :param choices: menu lines, len must be > 0 - :type choices: list of tuples (`tag`, `item`) tags must be unique or - list of items (tags will be enumerated) - - :param str ok_label: label of the OK button - :param str help_label: label of the help button - :param dict unused_kwargs: absorbs default / cli_args - - :returns: tuple of the form (`code`, `index`) where - `code` - display exit code - `int` - index of the selected item - :rtype: tuple - - """ - menu_options = { - "choices": choices, - "ok_label": ok_label, - "cancel_label": cancel_label, - "help_button": bool(help_label), - "help_label": help_label, - "width": self.width, - "height": self.height, - "menu_height": self.height - 6, - } - - # Can accept either tuples or just the actual choices - if choices and isinstance(choices[0], tuple): - # pylint: disable=star-args - code, selection = _clean(self.dialog.menu(message, **menu_options)) - - # Return the selection index - for i, choice in enumerate(choices): - if choice[0] == selection: - return code, i - - return code, -1 - - else: - # "choices" is not formatted the way the dialog.menu expects... - menu_options["choices"] = [ - (str(i), choice) for i, choice in enumerate(choices, 1) - ] - # pylint: disable=star-args - code, index = _clean(self.dialog.menu(message, **menu_options)) - - if code == CANCEL or index == "": - return code, -1 - - return code, int(index) - 1 - - def input(self, message, **unused_kwargs): - """Display an input box to the user. - - :param str message: Message to display that asks for input. - :param dict _kwargs: absorbs default / cli_args - - :returns: tuple of the form (`code`, `string`) where - `code` - display exit code - `string` - input entered by the user - - """ - return self.dialog.inputbox(message) - - def yesno(self, message, yes_label="Yes", no_label="No", **unused_kwargs): - """Display a Yes/No dialog box. - - Yes and No label must begin with different letters. - - :param str message: message to display to user - :param str yes_label: label on the "yes" button - :param str no_label: label on the "no" button - :param dict _kwargs: absorbs default / cli_args - - :returns: if yes_label was selected - :rtype: bool - - """ - return self.dialog.DIALOG_OK == self.dialog.yesno( - message, yes_label=yes_label, no_label=no_label) - - def checklist(self, message, tags, default_status=True, **unused_kwargs): - """Displays a checklist. - - :param message: Message to display before choices - :param list tags: where each is of type :class:`str` len(tags) > 0 - :param bool default_status: If True, items are in a selected state by - default. - :param dict _kwargs: absorbs default / cli_args - - - :returns: tuple of the form (`code`, `list_tags`) where - `code` - display exit code - `list_tags` - list of str tags selected by the user - - """ - choices = [(tag, "", default_status) for tag in tags] - return self.dialog.checklist(message, choices=choices) - - def directory_select(self, message, **unused_kwargs): - """Display a directory selection screen. - - :param str message: prompt to give the user - - :returns: tuple of the form (`code`, `string`) where - `code` - display exit code - `string` - input entered by the user - - """ - root_directory = os.path.abspath(os.sep) - return self.dialog.dselect( - filepath=root_directory, help_button=True, title=message) - - @zope.interface.implementer(interfaces.IDisplay) class FileDisplay(object): """File-based display.""" @@ -231,12 +55,11 @@ class FileDisplay(object): super(FileDisplay, self).__init__() self.outfile = outfile - def notification(self, message, height=10, pause=True): + def notification(self, message, pause=True): # pylint: disable=unused-argument """Displays a notification and waits for user acceptance. :param str message: Message to display - :param int height: No effect for FileDisplay :param bool pause: Whether or not the program should pause for the user's confirmation @@ -496,12 +319,11 @@ class NoninteractiveDisplay(object): msg += "\n\n(You can set this with the {0} flag)".format(cli_flag) raise errors.MissingCommandlineFlag(msg) - def notification(self, message, height=10, pause=False): + def notification(self, message, pause=False): # pylint: disable=unused-argument """Displays a notification without waiting for user acceptance. :param str message: Message to display to stdout - :param int height: No effect for NoninteractiveDisplay :param bool pause: The NoninteractiveDisplay waits for no keyboard """ diff --git a/certbot/interfaces.py b/certbot/interfaces.py index 42a952f10..5872c38d8 100644 --- a/certbot/interfaces.py +++ b/certbot/interfaces.py @@ -378,11 +378,10 @@ class IInstaller(IPlugin): class IDisplay(zope.interface.Interface): """Generic display.""" - def notification(message, height, pause): + def notification(message, pause): """Displays a string message :param str message: Message to display - :param int height: Height of dialog box if applicable :param bool pause: Whether or not the application should pause for confirmation (if available) diff --git a/certbot/log.py b/certbot/log.py deleted file mode 100644 index 62241254a..000000000 --- a/certbot/log.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Logging utilities.""" -import logging - -import dialog - -from certbot.display import util as display_util - - -class DialogHandler(logging.Handler): # pylint: disable=too-few-public-methods - """Logging handler using dialog info box. - - :ivar int height: Height of the info box (without padding). - :ivar int width: Width of the info box (without padding). - :ivar list lines: Lines to be displayed in the info box. - :ivar d: Instance of :class:`dialog.Dialog`. - - """ - - PADDING_HEIGHT = 2 - PADDING_WIDTH = 4 - - def __init__(self, level=logging.NOTSET, height=display_util.HEIGHT, - width=display_util.WIDTH - 4, d=None): - # Handler not new-style -> no super - logging.Handler.__init__(self, level) - self.height = height - self.width = width - # "dialog" collides with module name... - self.d = dialog.Dialog() if d is None else d - self.lines = [] - - def emit(self, record): - """Emit message to a dialog info box. - - Only show the last (self.height) lines; note that lines can wrap - at self.width, so a single line could actually be multiple - lines. - - """ - for line in self.format(record).splitlines(): - # check for lines that would wrap - cur_out = line - while len(cur_out) > self.width: - # find first space before self.width chars into cur_out - last_space_pos = cur_out.rfind(' ', 0, self.width) - - if last_space_pos == -1: - # no spaces, just cut them off at whatever - self.lines.append(cur_out[0:self.width]) - cur_out = cur_out[self.width:] - else: - # cut off at last space - self.lines.append(cur_out[0:last_space_pos]) - cur_out = cur_out[last_space_pos + 1:] - if cur_out != '': - self.lines.append(cur_out) - - # show last 16 lines - content = '\n'.join(self.lines[-self.height:]) - - # add the padding around the box - self.d.infobox( - content, self.height + self.PADDING_HEIGHT, - self.width + self.PADDING_WIDTH) diff --git a/certbot/main.py b/certbot/main.py index 5c8105ddd..aa9b1892f 100644 --- a/certbot/main.py +++ b/certbot/main.py @@ -1,7 +1,6 @@ """Certbot main entry point.""" from __future__ import print_function import atexit -import dialog import functools import logging.handlers import os @@ -27,7 +26,6 @@ from certbot import errors from certbot import hooks from certbot import interfaces from certbot import util -from certbot import log from certbot import reporter from certbot import renewal from certbot import storage @@ -614,14 +612,9 @@ def setup_log_file_handler(config, logfile, fmt): return handler, log_file_path -def _cli_log_handler(config, level, fmt): - if config.text_mode or config.noninteractive_mode or config.verb == "renew": - handler = colored_logging.StreamHandler() - handler.setFormatter(logging.Formatter(fmt)) - else: - handler = log.DialogHandler() - # dialog box is small, display as less as possible - handler.setFormatter(logging.Formatter("%(message)s")) +def _cli_log_handler(level, fmt): + handler = colored_logging.StreamHandler() + handler.setFormatter(logging.Formatter(fmt)) handler.setLevel(level) return handler @@ -641,7 +634,7 @@ def setup_logging(config): level = -config.verbose_count * 10 file_handler, log_file_path = setup_log_file_handler( config, logfile=logfile, fmt=file_fmt) - cli_handler = _cli_log_handler(config, level, cli_fmt) + cli_handler = _cli_log_handler(level, cli_fmt) # TODO: use fileConfig? @@ -687,10 +680,7 @@ def _handle_exception(exc_type, exc_value, trace, config): # Here we're passing a client or ACME error out to the client at the shell # Tell the user a bit about what happened, without overwhelming # them with a full traceback - if issubclass(exc_type, dialog.error): - err = exc_value.complete_message() - else: - err = traceback.format_exception_only(exc_type, exc_value)[0] + err = traceback.format_exception_only(exc_type, exc_value)[0] # Typical error from the ACME module: # acme.messages.Error: urn:ietf:params:acme:error:malformed :: The # request message was malformed :: Error creating new registration @@ -764,10 +754,8 @@ def main(cli_args=sys.argv[1:]): displayer = display_util.NoninteractiveDisplay(open(os.devnull, "w")) elif config.noninteractive_mode: displayer = display_util.NoninteractiveDisplay(sys.stdout) - elif config.text_mode: - displayer = display_util.FileDisplay(sys.stdout) else: - displayer = display_util.NcursesDisplay() + displayer = display_util.FileDisplay(sys.stdout) zope.component.provideUtility(displayer) # Reporter diff --git a/certbot/plugins/manual.py b/certbot/plugins/manual.py index 2ef49d7f4..48aec6e35 100644 --- a/certbot/plugins/manual.py +++ b/certbot/plugins/manual.py @@ -243,7 +243,7 @@ s.serve_forever()" """ # pylint: disable=no-self-use # TODO: IDisplay wraps messages, breaking the command #answer = zope.component.getUtility(interfaces.IDisplay).notification( - # message=message, height=25, pause=True) + # message=message, pause=True) sys.stdout.write(message) six.moves.input("Press ENTER to continue") diff --git a/certbot/plugins/selection.py b/certbot/plugins/selection.py index 3fbc510ba..8f371f586 100644 --- a/certbot/plugins/selection.py +++ b/certbot/plugins/selection.py @@ -119,8 +119,7 @@ def choose_plugin(prepared, question): z_util(interfaces.IDisplay).notification( "The selected plugin encountered an error while parsing " "your server configuration and cannot be used. The error " - "was:\n\n{0}".format(plugin_ep.prepare()), - height=display_util.HEIGHT, pause=False) + "was:\n\n{0}".format(plugin_ep.prepare()), pause=False) else: return plugin_ep elif code == display_util.HELP: @@ -128,8 +127,7 @@ def choose_plugin(prepared, question): msg = "Reported Error: %s" % prepared[index].prepare() else: msg = prepared[index].init().more_info() - z_util(interfaces.IDisplay).notification( - msg, height=display_util.HEIGHT) + z_util(interfaces.IDisplay).notification(msg) else: return None diff --git a/certbot/plugins/util.py b/certbot/plugins/util.py index 786f6ca92..8f6a62a7f 100644 --- a/certbot/plugins/util.py +++ b/certbot/plugins/util.py @@ -103,7 +103,7 @@ def already_listening_socket(port, renewer=False): "Port {0} is already in use by another process. This will " "prevent us from binding to that port. Please stop the " "process that is populating the port in question and try " - "again. {1}".format(port, extra), height=13) + "again. {1}".format(port, extra)) return True finally: testsocket.close() @@ -151,8 +151,7 @@ def already_listening_psutil(port, renewer=False): "The program {0} (process ID {1}) is already listening " "on TCP port {2}. This will prevent us from binding to " "that port. Please stop the {0} program temporarily " - "and then try again.{3}".format(name, pid, port, extra), - height=13) + "and then try again.{3}".format(name, pid, port, extra)) return True except (psutil.NoSuchProcess, psutil.AccessDenied): # Perhaps the result of a race where the process could have diff --git a/certbot/plugins/webroot.py b/certbot/plugins/webroot.py index 1cd1d879a..2c449fdca 100644 --- a/certbot/plugins/webroot.py +++ b/certbot/plugins/webroot.py @@ -140,9 +140,8 @@ to serve all files under specified web root ({0}).""" code, webroot = display.directory_select( "Input the webroot for {0}:".format(domain)) if code == display_util.HELP: - # Help can currently only be selected - # when using the ncurses interface - display.notification(display_util.DSELECT_HELP) + # Displaying help is not currently implemented + return None elif code == display_util.CANCEL: return None else: # code == display_util.OK diff --git a/certbot/plugins/webroot_test.py b/certbot/plugins/webroot_test.py index 5d784a75c..2aa7e8acc 100644 --- a/certbot/plugins/webroot_test.py +++ b/certbot/plugins/webroot_test.py @@ -111,8 +111,7 @@ class AuthenticatorTest(unittest.TestCase): self.assertTrue(mock_display.notification.called) for call in mock_display.notification.call_args_list: - self.assertTrue(imaginary_dir in call[0][0] or - display_util.DSELECT_HELP == call[0][0]) + self.assertTrue(imaginary_dir in call[0][0]) self.assertTrue(mock_display.directory_select.called) for call in mock_display.directory_select.call_args_list: diff --git a/certbot/reverter.py b/certbot/reverter.py index 098c74911..714a38b8b 100644 --- a/certbot/reverter.py +++ b/certbot/reverter.py @@ -15,8 +15,6 @@ from certbot import errors from certbot import interfaces from certbot import util -from certbot.display import util as display_util - logger = logging.getLogger(__name__) @@ -183,7 +181,7 @@ class Reverter(object): if for_logging: return os.linesep.join(output) zope.component.getUtility(interfaces.IDisplay).notification( - os.linesep.join(output), display_util.HEIGHT) + os.linesep.join(output)) def add_to_temp_checkpoint(self, save_files, save_notes): """Add files to temporary checkpoint. diff --git a/certbot/tests/cli_test.py b/certbot/tests/cli_test.py index 5d2051cfe..ce01d69ff 100644 --- a/certbot/tests/cli_test.py +++ b/certbot/tests/cli_test.py @@ -5,7 +5,6 @@ from __future__ import print_function import argparse -import dialog import functools import itertools import os @@ -51,6 +50,7 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.config_dir = os.path.join(self.tmp_dir, 'config') self.work_dir = os.path.join(self.tmp_dir, 'work') self.logs_dir = os.path.join(self.tmp_dir, 'logs') + os.mkdir(self.logs_dir) self.standard_args = ['--config-dir', self.config_dir, '--work-dir', self.work_dir, '--logs-dir', self.logs_dir, '--text'] @@ -98,6 +98,8 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue("--configurator" in out) self.assertTrue("how a cert is deployed" in out) self.assertTrue("--manual-test-mode" in out) + self.assertTrue("--text" not in out) + self.assertTrue("--dialog" not in out) out = self._help_output(['-h', 'nginx']) if "nginx" in plugins: @@ -163,12 +165,11 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self._cli_missing_flag(args, "--agree-tos") @mock.patch('certbot.main.renew') - def test_gui(self, renew): + def test_no_gui(self, renew): args = ['renew', '--dialog'] - # --text conflicts with --dialog - self.standard_args.remove('--text') + # --dialog should have no effect self._call(args) - self.assertFalse(renew.call_args[0][0].noninteractive_mode) + self.assertTrue(renew.call_args[0][0].noninteractive_mode) @mock.patch('certbot.main.client.acme_client.Client') @mock.patch('certbot.main._determine_account') @@ -656,9 +657,11 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods log_out="not yet due", should_renew=False) def _dump_log(self): - with open(os.path.join(self.logs_dir, "letsencrypt.log")) as lf: - print("Logs:") - print(lf.read()) + print("Logs:") + log_path = os.path.join(self.logs_dir, "letsencrypt.log") + if os.path.exists(log_path): + with open(log_path) as lf: + print(lf.read()) def _make_lineage(self, testfile): """Creates a lineage defined by testfile. @@ -977,13 +980,6 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods mock_sys.exit.assert_called_with(''.join( traceback.format_exception_only(KeyboardInterrupt, interrupt))) - # Test dialog errors - exception = dialog.error(message="test message") - main._handle_exception( - dialog.DialogError, exc_value=exception, trace=None, config=None) - error_msg = mock_sys.exit.call_args_list[-1][0][0] - self.assertTrue("test message" in error_msg) - def test_read_file(self): rel_test_path = os.path.relpath(os.path.join(self.tmp_dir, 'foo')) self.assertRaises( @@ -1070,17 +1066,6 @@ class CLITest(unittest.TestCase): # pylint: disable=too-many-public-methods self.assertTrue( email in mock_utility().add_message.call_args[0][0]) - def test_conflicting_args(self): - args = ['renew', '--dialog', '--text'] - self.assertRaises(errors.Error, self._call, args) - - def test_text_mode_when_verbose(self): - parse = self._get_argument_parser() - short_args = ['-v'] - namespace = parse(short_args) - self.assertTrue(namespace.text_mode) - - class DetermineAccountTest(unittest.TestCase): """Tests for certbot.cli._determine_account.""" diff --git a/certbot/tests/display/util_test.py b/certbot/tests/display/util_test.py index c1d33eff4..b04235bd9 100644 --- a/certbot/tests/display/util_test.py +++ b/certbot/tests/display/util_test.py @@ -13,122 +13,6 @@ CHOICES = [("First", "Description1"), ("Second", "Description2")] TAGS = ["tag1", "tag2", "tag3"] TAGS_CHOICES = [("1", "tag1"), ("2", "tag2"), ("3", "tag3")] - -class NcursesDisplayTest(unittest.TestCase): - """Test ncurses display. - - Since this is mostly a wrapper, it might be more helpful to test the - actual dialog boxes. The test file located in ./tests/display.py - (relative to the root of the repository) will actually display the - various boxes but requires the user to do the verification. If - something seems amiss please use that test script to debug it, the - automatic tests rely on too much mocking. - - """ - def setUp(self): - super(NcursesDisplayTest, self).setUp() - self.displayer = display_util.NcursesDisplay() - - self.default_menu_options = { - "choices": CHOICES, - "ok_label": "OK", - "cancel_label": "Cancel", - "help_button": False, - "help_label": "", - "width": display_util.WIDTH, - "height": display_util.HEIGHT, - "menu_height": display_util.HEIGHT - 6, - } - - @mock.patch("certbot.display.util.dialog.Dialog.msgbox") - def test_notification(self, mock_msgbox): - """Kind of worthless... one liner.""" - self.displayer.notification("message") - self.assertEqual(mock_msgbox.call_count, 1) - - @mock.patch("certbot.display.util.dialog.Dialog.menu") - def test_menu_tag_and_desc(self, mock_menu): - mock_menu.return_value = (display_util.OK, "First") - - ret = self.displayer.menu("Message", CHOICES) - mock_menu.assert_called_with("Message", **self.default_menu_options) - - self.assertEqual(ret, (display_util.OK, 0)) - - @mock.patch("certbot.display.util.dialog.Dialog.menu") - def test_menu_tag_and_desc_cancel(self, mock_menu): - mock_menu.return_value = (display_util.CANCEL, "") - - ret = self.displayer.menu("Message", CHOICES) - - mock_menu.assert_called_with("Message", **self.default_menu_options) - - self.assertEqual(ret, (display_util.CANCEL, -1)) - - @mock.patch("certbot.display.util.dialog.Dialog.menu") - def test_menu_desc_only(self, mock_menu): - mock_menu.return_value = (display_util.OK, "1") - - ret = self.displayer.menu("Message", TAGS, help_label="More Info") - - self.default_menu_options.update( - choices=TAGS_CHOICES, help_button=True, help_label="More Info") - mock_menu.assert_called_with("Message", **self.default_menu_options) - - self.assertEqual(ret, (display_util.OK, 0)) - - @mock.patch("certbot.display.util.dialog.Dialog.menu") - def test_menu_desc_only_help(self, mock_menu): - mock_menu.return_value = (display_util.HELP, "2") - - ret = self.displayer.menu("Message", TAGS, help_label="More Info") - - self.assertEqual(ret, (display_util.HELP, 1)) - - @mock.patch("certbot.display.util.dialog.Dialog.menu") - def test_menu_desc_only_cancel(self, mock_menu): - mock_menu.return_value = (display_util.CANCEL, "") - - ret = self.displayer.menu("Message", TAGS, help_label="More Info") - - self.assertEqual(ret, (display_util.CANCEL, -1)) - - @mock.patch("certbot.display.util." - "dialog.Dialog.inputbox") - def test_input(self, mock_input): - mock_input.return_value = (mock.MagicMock(), mock.MagicMock()) - self.displayer.input("message") - self.assertEqual(mock_input.call_count, 1) - - @mock.patch("certbot.display.util.dialog.Dialog.yesno") - def test_yesno(self, mock_yesno): - mock_yesno.return_value = display_util.OK - - self.assertTrue(self.displayer.yesno("message")) - - mock_yesno.assert_called_with( - "message", yes_label="Yes", no_label="No") - - @mock.patch("certbot.display.util." - "dialog.Dialog.checklist") - def test_checklist(self, mock_checklist): - mock_checklist.return_value = (mock.MagicMock(), mock.MagicMock()) - self.displayer.checklist("message", TAGS) - - choices = [ - (TAGS[0], "", True), - (TAGS[1], "", True), - (TAGS[2], "", True), - ] - mock_checklist.assert_called_with("message", choices=choices) - - @mock.patch("certbot.display.util.dialog.Dialog.dselect") - def test_directory_select(self, mock_dselect): - mock_dselect.return_value = (mock.MagicMock(), mock.MagicMock()) - self.displayer.directory_select("message") - self.assertEqual(mock_dselect.call_count, 1) - - class FileOutputDisplayTest(unittest.TestCase): """Test stdout display. @@ -142,7 +26,7 @@ class FileOutputDisplayTest(unittest.TestCase): self.displayer = display_util.FileDisplay(self.mock_stdout) def test_notification_no_pause(self): - self.displayer.notification("message", 10, False) + self.displayer.notification("message", False) string = self.mock_stdout.write.call_args[0][0] self.assertTrue("message" in string) diff --git a/certbot/tests/log_test.py b/certbot/tests/log_test.py deleted file mode 100644 index a4f394870..000000000 --- a/certbot/tests/log_test.py +++ /dev/null @@ -1,48 +0,0 @@ -"""Tests for certbot.log.""" -import logging -import unittest - -import mock - - -class DialogHandlerTest(unittest.TestCase): - - def setUp(self): - self.d = mock.MagicMock() - - from certbot.log import DialogHandler - self.handler = DialogHandler(height=2, width=6, d=self.d) - self.handler.PADDING_HEIGHT = 2 - self.handler.PADDING_WIDTH = 4 - - def test_adds_padding(self): - self.handler.emit(logging.makeLogRecord({})) - self.d.infobox.assert_called_once_with(mock.ANY, 4, 10) - - def test_args_in_msg_get_replaced(self): - assert len('123456') <= self.handler.width - self.handler.emit(logging.makeLogRecord( - {'msg': '123%s', 'args': (456,)})) - self.d.infobox.assert_called_once_with('123456', mock.ANY, mock.ANY) - - def test_wraps_nospace_is_greedy(self): - assert len('1234567') > self.handler.width - self.handler.emit(logging.makeLogRecord({'msg': '1234567'})) - self.d.infobox.assert_called_once_with('123456\n7', mock.ANY, mock.ANY) - - def test_wraps_at_whitespace(self): - assert len('123 567') > self.handler.width - self.handler.emit(logging.makeLogRecord({'msg': '123 567'})) - self.d.infobox.assert_called_once_with('123\n567', mock.ANY, mock.ANY) - - def test_only_last_lines_are_printed(self): - assert len('a\nb\nc'.split()) > self.handler.height - self.handler.emit(logging.makeLogRecord({'msg': 'a\n\nb\nc'})) - self.d.infobox.assert_called_once_with('b\nc', mock.ANY, mock.ANY) - - def test_non_str(self): - self.handler.emit(logging.makeLogRecord({'msg': {'foo': 'bar'}})) - - -if __name__ == '__main__': - unittest.main() # pragma: no cover diff --git a/certbot/tests/main_test.py b/certbot/tests/main_test.py index f7a6c5896..045a593e9 100644 --- a/certbot/tests/main_test.py +++ b/certbot/tests/main_test.py @@ -11,7 +11,6 @@ from certbot import colored_logging from certbot import constants from certbot import configuration from certbot import errors -from certbot import log from certbot.plugins import disco as plugins_disco class MainTest(unittest.TestCase): @@ -55,9 +54,9 @@ class ObtainCertTest(unittest.TestCase): mock_notification = self.mock_get_utility().notification mock_notification.side_effect = self._assert_no_pause mock_auth.return_value = ('reinstall', mock.ANY) - self._call('certonly --webroot -d example.com -t'.split()) + self._call('certonly --webroot -d example.com'.split()) - def _assert_no_pause(self, message, height=42, pause=True): + def _assert_no_pause(self, message, pause=True): # pylint: disable=unused-argument self.assertFalse(pause) @@ -89,7 +88,7 @@ class SetupLoggingTest(unittest.TestCase): def setUp(self): self.config = mock.Mock( logs_dir=tempfile.mkdtemp(), - noninteractive_mode=False, quiet=False, text_mode=False, + noninteractive_mode=False, quiet=False, verbose_count=constants.CLI_DEFAULTS['verbose_count']) def tearDown(self): @@ -107,7 +106,7 @@ class SetupLoggingTest(unittest.TestCase): cli_handler = mock_get_logger().addHandler.call_args_list[0][0][0] self.assertEqual(cli_handler.level, -self.config.verbose_count * 10) self.assertTrue( - isinstance(cli_handler, log.DialogHandler)) + isinstance(cli_handler, colored_logging.StreamHandler)) @mock.patch('certbot.main.logging.getLogger') def test_quiet_mode(self, mock_get_logger): diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index b5adeea86..4ec226d39 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -248,7 +248,6 @@ BootstrapDebCommon() { python-dev \ $virtualenv \ gcc \ - dialog \ $augeas_pkg \ libssl-dev \ libffi-dev \ @@ -307,7 +306,6 @@ BootstrapRpmCommon() { pkgs=" gcc - dialog augeas-libs openssl openssl-devel @@ -361,7 +359,6 @@ BootstrapSuseCommon() { python-devel \ python-virtualenv \ gcc \ - dialog \ augeas-lenses \ libopenssl-devel \ libffi-devel \ @@ -380,7 +377,6 @@ BootstrapArchCommon() { python2 python-virtualenv gcc - dialog augeas openssl libffi @@ -404,7 +400,6 @@ BootstrapGentooCommon() { PACKAGES=" dev-lang/python:2.7 dev-python/virtualenv - dev-util/dialog app-admin/augeas dev-libs/openssl dev-libs/libffi @@ -449,7 +444,6 @@ BootstrapMac() { fi $pkgcmd augeas - $pkgcmd dialog if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" \ -o "$(which python)" = "/usr/bin/python" ]; then # We want to avoid using the system Python because it requires root to use pip. @@ -496,7 +490,6 @@ BootstrapMageiaCommon() { if ! $SUDO urpmi --force \ git \ gcc \ - cdialog \ python-augeas \ libopenssl-devel \ libffi-devel \ @@ -701,9 +694,6 @@ pyRFC3339==1.0 \ --hash=sha256:8dfbc6c458b8daba1c0f3620a8c78008b323a268b27b7359e92a4ae41325f535 python-augeas==0.5.0 \ --hash=sha256:67d59d66cdba8d624e0389b87b2a83a176f21f16a87553b50f5703b23f29bac2 -python2-pythondialog==3.3.0 \ - --hash=sha256:04e93f24995c43dd90f338d5d865ca72ce3fb5a5358d4daa4965571db35fc3ec \ - --hash=sha256:3e6f593fead98f8a526bc3e306933533236e33729f552f52896ea504f55313fa pytz==2015.7 \ --hash=sha256:3abe6a6d3fc2fbbe4c60144211f45da2edbe3182a6f6511af6bbba0598b1f992 \ --hash=sha256:939ef9c1e1224d980405689a97ffcf7828c56d1517b31d73464356c1f2b7769e \ diff --git a/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh index 39e2da5fe..333f56ff7 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/arch_common.sh @@ -10,7 +10,6 @@ BootstrapArchCommon() { python2 python-virtualenv gcc - dialog augeas openssl libffi diff --git a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh index 8eb7e16ee..0188cadc5 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/deb_common.sh @@ -94,7 +94,6 @@ BootstrapDebCommon() { python-dev \ $virtualenv \ gcc \ - dialog \ $augeas_pkg \ libssl-dev \ libffi-dev \ diff --git a/letsencrypt-auto-source/pieces/bootstrappers/gentoo_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/gentoo_common.sh index 580b69a0d..4b1e3b545 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/gentoo_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/gentoo_common.sh @@ -2,7 +2,6 @@ BootstrapGentooCommon() { PACKAGES=" dev-lang/python:2.7 dev-python/virtualenv - dev-util/dialog app-admin/augeas dev-libs/openssl dev-libs/libffi diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh index 2b04977c8..a28b410d2 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/mac.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mac.sh @@ -15,7 +15,6 @@ BootstrapMac() { fi $pkgcmd augeas - $pkgcmd dialog if [ "$(which python)" = "/System/Library/Frameworks/Python.framework/Versions/2.7/bin/python" \ -o "$(which python)" = "/usr/bin/python" ]; then # We want to avoid using the system Python because it requires root to use pip. diff --git a/letsencrypt-auto-source/pieces/bootstrappers/mageia_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/mageia_common.sh index d6651574a..63649a974 100644 --- a/letsencrypt-auto-source/pieces/bootstrappers/mageia_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/mageia_common.sh @@ -11,7 +11,6 @@ BootstrapMageiaCommon() { if ! $SUDO urpmi --force \ git \ gcc \ - cdialog \ python-augeas \ libopenssl-devel \ libffi-devel \ diff --git a/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh index 2fd629ff8..26d717ea1 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/rpm_common.sh @@ -43,7 +43,6 @@ BootstrapRpmCommon() { pkgs=" gcc - dialog augeas-libs openssl openssl-devel diff --git a/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh b/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh index 9ac295922..bd4d9c68d 100755 --- a/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh +++ b/letsencrypt-auto-source/pieces/bootstrappers/suse_common.sh @@ -11,7 +11,6 @@ BootstrapSuseCommon() { python-devel \ python-virtualenv \ gcc \ - dialog \ augeas-lenses \ libopenssl-devel \ libffi-devel \ diff --git a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt index 1803d51b8..de83178b0 100644 --- a/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt +++ b/letsencrypt-auto-source/pieces/letsencrypt-auto-requirements.txt @@ -110,9 +110,6 @@ pyRFC3339==1.0 \ --hash=sha256:8dfbc6c458b8daba1c0f3620a8c78008b323a268b27b7359e92a4ae41325f535 python-augeas==0.5.0 \ --hash=sha256:67d59d66cdba8d624e0389b87b2a83a176f21f16a87553b50f5703b23f29bac2 -python2-pythondialog==3.3.0 \ - --hash=sha256:04e93f24995c43dd90f338d5d865ca72ce3fb5a5358d4daa4965571db35fc3ec \ - --hash=sha256:3e6f593fead98f8a526bc3e306933533236e33729f552f52896ea504f55313fa pytz==2015.7 \ --hash=sha256:3abe6a6d3fc2fbbe4c60144211f45da2edbe3182a6f6511af6bbba0598b1f992 \ --hash=sha256:939ef9c1e1224d980405689a97ffcf7828c56d1517b31d73464356c1f2b7769e \ diff --git a/setup.py b/setup.py index 6d0909ea8..f2d021c97 100644 --- a/setup.py +++ b/setup.py @@ -51,12 +51,6 @@ install_requires = [ 'zope.interface', ] -# Debian squeeze support, cf. #280 -if sys.version_info[0] == 2: - install_requires.append('python2-pythondialog>=3.2.2rc1') -else: - install_requires.append('pythondialog>=3.2.2rc1') - # env markers in extras_require cause problems with older pip: #517 # Keep in sync with conditional_requirements.py. if sys.version_info < (2, 7): diff --git a/tests/display.py b/tests/display.py index ecb7c279b..7400788a3 100644 --- a/tests/display.py +++ b/tests/display.py @@ -18,5 +18,5 @@ def test_visual(displayer, choices): if __name__ == "__main__": - for displayer in util.NcursesDisplay(), util.FileDisplay(sys.stdout): - test_visual(displayer, util_test.CHOICES) + displayer = util.FileDisplay(sys.stdout): + test_visual(displayer, util_test.CHOICES) diff --git a/tox.ini b/tox.ini index ac39e995e..162601205 100644 --- a/tox.ini +++ b/tox.ini @@ -38,7 +38,6 @@ deps = py{26,27}-oldest: dnspython>=1.12 py{26,27}-oldest: psutil==2.1.0 py{26,27}-oldest: PyOpenSSL==0.13 - py{26,27}-oldest: python2-pythondialog==3.2.2rc1 [testenv:py33] commands =