def _printer(self, what, columns=-1): """Print WHAT in an appropriate way, wrapping to the specified number of COLUMNS. Override this function to change its behavior. """ if columns == 0: # Wrapping is totally disabled. Print exactly as generated. log_it("INFO: COLUMNS is zero; not wrapping text at all", 2) print(what) else: if columns == -1: # Wrap to best guess for terminal width log_it( "INFO: COLUMNS is -1; wrapping text to best-guess column width", 2) padding = 0 else: # Wrap to specified width (unless current terminal width is odd, in which case we're off by 1. Oh well.) padding = max((th.terminal_width() - columns) // 2, 0) log_it( "INFO: COLUMNS is %s; padding text with %s spaces on each side" % (columns, padding), 2) log_it("NOTE: terminal width is %s" % th.terminal_width(), 2) what = th.multi_replace(what, [ ['\n\n', '\n'], ]) # Last chance to postprocess text is right here for the_paragraph in what.split('\n'): if the_paragraph: # Skip any empty paragraphs that may pop up th.print_indented(the_paragraph, each_side=padding) print()
def __init__(self, name="default logger", logfile_paths=None): """Set up the logger object. If LOGFILE_PATHS is specified, a log file is opened and kept at each location in the list. """ self.width = text_handling.terminal_width() self.name = name self.output_destinations = [sys.stdout] if logfile_paths is None: self.logfile_paths = [][:] elif isinstance(logfile_paths, (list, tuple)): self.logfile_paths = logfile_paths else: self.logfile_paths = [logfile_paths] for logfile_path in self.logfile_paths: new_log_file = open(logfile_path, mode='wt', buffering=1) self.output_destinations += [new_log_file] new_log_file.write( 'Hello, traveler. This is the beginning of a log file called: "%s".\n' % self.name) new_log_file.write( 'Some basic system info about the host machine: %s\n' % str(os.uname())) new_log_file.write( 'This log was begun %s\n\n\n\n' % datetime.datetime.now().strftime('%A, %d %B %Y at %H:%M')) self.width = -1
def menu_choice(choice_menu, prompt): """Takes a menu description, passed in as CHOICE_MENU (see below for format), and asks the user to make a choice between the options. It then passes back the user's choice to the caller. :param choice_menu: an OrderedDict that maps a list of options to be typed (short strings, each of which is ideally a single letter) to a full description of what that option means (a longer string). For example: OrderedDict([ ('a', 'always capitalize'), ('y', 'yes'), ('n', 'never') ]) as a special case, if both parts of an entry in the OrderedDict are two hyphens, that entry is not a valid menu choice; it is printed as-is, as a visual separator, but is not a selectable option. :param prompt: a direct request for input; printed after all of the menu options have been displayed. :return: a string: the response the user typed that was validated as an allowed choice. """ max_menu_item_width = max(len(x) for x in choice_menu) menu_column_width = max_menu_item_width + len(" [ ") + len(" ]") spacing_column_width = 3 options_column_width = text_handling.terminal_width() - (menu_column_width + spacing_column_width + 1) # OK, let's print this menu. print() for option, text in choice_menu.items(): if (option == '--') and (text == '--'): current_line = ' -- ' + ' ' * (max_menu_item_width - len('--')) + ' ' * spacing_column_width + '-----' else: current_line = '[ %s ]%s%s' % (option, ' ' * (max_menu_item_width - len(option)), ' ' * spacing_column_width) text_lines = text_handling._get_wrapped_lines(text, enclosing_width=options_column_width) if len(text_lines) == 1: current_line = current_line + text_lines[0] else: current_line = current_line + text_lines.pop(0) # Finish the line with the first line of the description left_padding = '\n' + (' ' * (menu_column_width + spacing_column_width)) current_line = current_line + left_padding + left_padding.join(text_lines) # Add in the rest of the lines print(current_line) print() patrick_logger.log_it("INFO: multi_choice_menu.py: menu laid out in %d lines." % len(current_line.split('\n')), 2) patrick_logger.log_it("INFO: multi_choice_menu.py: menu contents are: %s" % current_line, 4) # Now, get the user's choice choice = 'not a legal option' legal_options = [ k.lower() for k, v in choice_menu.items() if ((k != '--') or (v != '--')) ] patrick_logger.log_it("INFO: multi_choice_menu.py: Legal options for this menu are %s" % legal_options, 2) tried_yet = False while choice.lower() not in legal_options: if tried_yet: # If the user has got it wrong at least once... prompt = prompt.strip() + " [ %s ] " % ('/'.join(legal_options)) choice = input(prompt.strip() + " ").strip() tried_yet = True return choice
def menu_choice(choice_menu, prompt): """Takes a menu description, passed in as CHOICE_MENU (see below for format), and asks the user to make a choice between the options. It then passes back the user's choice to the caller. :param choice_menu: an OrderedDict that maps a list of options to be typed (short strings, each of which is ideally a single letter) to a full description of what that option means (a longer string). For example: OrderedDict([ ('a', 'always capitalize'), ('y', 'yes'), ('n', 'never') ]) :param prompt: a direct request for input printed after all of the menu options have been displayed. :return: a string: the response the user typed that was validated as an allowed choice. """ max_menu_item_width = max(len(x) for x in choice_menu) menu_column_width = max_menu_item_width + len(" [ ") + len(" ]") spacing_column_width = 3 options_column_width = text_handling.terminal_width() - (menu_column_width + spacing_column_width + 1) # OK, let's print this menu. print() for option, text in choice_menu.items(): current_line = '[ %s ]%s%s' % (option, ' ' * (max_menu_item_width - len(option)), ' ' * spacing_column_width) text_lines = text_handling._get_wrapped_lines(text, enclosing_width=options_column_width) if len(text_lines) == 1: current_line = current_line + text_lines[0] else: current_line = current_line + text_lines.pop(0) # Finish the line with the first line of the description left_padding = '\n' + (' ' * (menu_column_width + spacing_column_width)) current_line = current_line + left_padding.join(text_lines) # Add in the rest of the description lines print(current_line) print() patrick_logger.log_it("INFO: multi_choice_menu.py: menu laid out in %d lines." % len(current_line.split('\n')), 2) patrick_logger.log_it("INFO: multi_choice_menu.py: menu contents are: %s" % current_line, 4) # Now, get the user's choice choice = 'not a legal option' legal_options = [ l.lower() for l in choice_menu ] patrick_logger.log_it("INFO: multi_choice_menu.py: Legal options for this menu are %s" % legal_options, 2) tried_yet = False while choice.lower() not in legal_options: if tried_yet: # If the user has got it wrong at least once. prompt = prompt.strip() + " [ %s ] " % ('/'.join(choice_menu)) choice = input(prompt.strip() + " ").strip() tried_yet = True return choice
current_line = current_line + left_padding + left_padding.join(text_lines) # Add in the rest of the lines print(current_line) print() patrick_logger.log_it("INFO: multi_choice_menu.py: menu laid out in %d lines." % len(current_line.split('\n')), 2) patrick_logger.log_it("INFO: multi_choice_menu.py: menu contents are: %s" % current_line, 4) # Now, get the user's choice choice = 'not a legal option' legal_options = [ k.lower() for k, v in choice_menu.items() if ((k != '--') or (v != '--')) ] patrick_logger.log_it("INFO: multi_choice_menu.py: Legal options for this menu are %s" % legal_options, 2) tried_yet = False while choice.lower() not in legal_options: if tried_yet: # If the user has got it wrong at least once... prompt = prompt.strip() + " [ %s ] " % ('/'.join(legal_options)) choice = input(prompt.strip() + " ").strip() tried_yet = True return choice if __name__ == "__main__": patrick_logger.verbosity_level = 3 print("INFO: Terminal width is %d.\n" % text_handling.terminal_width()) response = "N" the_menu = OrderedDict([ ('Y', 'Yes, I do'), ('N', 'No, not yet') ]) while response.lower() == "n": response = menu_choice(the_menu, "You do understand that this is not a program itself, but rather a utility for other programs to use, don't you?") print("\nYou chose '%s'." % response)
current_line = current_line + left_padding.join(text_lines) # Add in the rest of the description lines print(current_line) print() patrick_logger.log_it("INFO: multi_choice_menu.py: menu laid out in %d lines." % len(current_line.split('\n')), 2) patrick_logger.log_it("INFO: multi_choice_menu.py: menu contents are: %s" % current_line, 4) # Now, get the user's choice choice = 'not a legal option' legal_options = [ l.lower() for l in choice_menu ] patrick_logger.log_it("INFO: multi_choice_menu.py: Legal options for this menu are %s" % legal_options, 2) tried_yet = False while choice.lower() not in legal_options: if tried_yet: # If the user has got it wrong at least once. prompt = prompt.strip() + " [ %s ] " % ('/'.join(choice_menu)) choice = input(prompt.strip() + " ").strip() tried_yet = True return choice if __name__ == "__main__": patrick_logger.verbosity_level = 3 print("INFO: Terminal width is %d.\n" % text_handling.terminal_width()) response = "N" the_menu = OrderedDict([ ('Y', 'Yes, I do'), ('N', 'No, not yet') ]) while response.lower() == "n": response = menu_choice(the_menu, "You do understand that this is not a program itself, but rather a utility for other programs to use, don't you?") print("\nYou chose '%s'." % response)