""" Information ----------- This is a small project to help with some console functionality. It allows for printing a progress bar to the console as well as provides a utility for printing console output with the program name pre-pended to the output. Python Module Usage ------------------- """ import sys import argparse from datetime import datetime as dt class CustomPrintAction(argparse.Action): def __init__(self, option_strings, print_fn, help, dest): self.version_fn = print_fn super(CustomPrintAction, self).__init__(option_strings, dest=argparse.SUPPRESS, default=argparse.SUPPRESS, nargs=0, help=help) def __call__(self, parser, args, values, option_string=None): self.version_fn() parser.exit() class SLPrinter: def __init__(self, prog_name, timestamps=True, out=sys.stdout, tabsize=3, maxlen=None): self.old_stdout = out self.prog_name = prog_name self.log_time = timestamps self.date_format = '%Y/%m/%d %H:%M:%S' self.tab_str = ' ' * tabsize self.max_len = maxlen def write_tabbed(self, text, num_indents): self._write_to_output(self._make_text(text, num_indents)) def write(self, text): text = self._make_text(text) if len(text) == 0: return self._write_to_output(text) def write_no_prefix(self, text): if not text.endswith('\n'): text += '\n' self._write_to_output(text) def flush(self): self.old_stdout.flush() def write_line_break(self, break_char='-', line_size=80): self._write_to_output('%s\n' % (break_char * line_size)) def _make_text(self, text, tab_factor=0): text = text.replace('\n', '') # If we just want a blank line... raw_txt_len = len(text) if raw_txt_len == 0: return '' if self.max_len is not None: if raw_txt_len > self.max_len: first_line = text[:self.max_len] second_line = text[self.max_len:] while second_line.startswith(' '): second_line = second_line[1:] text = '%s\n%s' % (first_line, self._make_text(second_line, tab_factor=tab_factor)) text = '[%s]%s %s\n' % (self.prog_name, (self.tab_str * tab_factor), text.rstrip()) if self.log_time: text = '%s %s' % (dt.now().strftime(self.date_format), text) # if self.max_len is not None: # # If the text is too long... # if raw_txt_len > self.max_len + 1: # # Get the next line of text and format it with a recursive call # first_line = text[:self.max_len + 1] # second_line = text[self.max_len + 2:] # text = '%s\n%s' % (first_line, self.__make_text__(second_line)) return text # This is the only part which needs to be overridden to add logging to files or other output piping/analysis def _write_to_output(self, text): self.old_stdout.write(text) class SLLogger(SLPrinter): def __init__(self, prog_name, log_path, timestamps=True, out=sys.stdout): super().__init__(prog_name, timestamps, out) self.log_file = log_path # Overriding this here because we want to save the output to a file. def __write_to_output__(self, text): super().__write_to_output__(text) with open(self.log_file, 'a+') as logger: logger.write(text) # Returns a green progress bar with a percentage on the left. def get_prg_line(current, total, bar_size, line_size, percent_decimals=2): num_to_repeat = int(bar_size*(current/total)) if num_to_repeat == 0: num_to_repeat = 1 if current == total: tmp_result = '{0:} '.format('Done!') + '\033[102m' + (' ' * num_to_repeat) + '\033[0m' + \ (' ' * (bar_size - num_to_repeat) + '\n') else: tmp_result = ('{1:.{0}%} '.format(percent_decimals, current/total) + '\033[102m' + (' ' * num_to_repeat) + ('\033[0m' + (' ' * (bar_size - num_to_repeat)))) final_result = ('{:^%d}' % line_size).format(tmp_result) return final_result # Writes a progress bar to the console with the given info and a % on the left. # This will update if it is the only line printed! def print_progress_bar(current, total, bar_size, line_size, precision=2): sys.stdout.write('\r%s' % ' ' * line_size) sys.stdout.write('\r%s' % get_prg_line(current, total, bar_size, line_size, precision)) def yes_or_no(message): """ Will prompt the user with the given message and accept either ``"y"``, ``"yes"``, ``"n"``, or ``"no"`` as inputs ignoring case. The program will exit (with status 0) if the user enters ``"no"`` and will continue if the user enters ``"yes"``. Args: ``message`` -- ``str`` The message to display. Returns: ``None`` *NOTE*: The user will be prompted to re-enter input if it is not valid with the following message (until valid input is provided): ``"Invalid input, enter Y(es) or N(o):"`` """ decision = input('%s yes/no: ' % message) if decision.lower() == 'y' or decision.lower() == 'yes': return elif decision.lower() == 'n' or decision.lower() == 'no': print('Exiting...') exit(0) else: yes_or_no(' Invalid input, enter Y(es) or N(o):\n%s' % message) def get_header(program_name, program_version=None, build_date=None, program_author=None, line_width=80): """ Generates a header in the following format: :: +--------------------------------------+ | program_name | +--------------------------------------+ | Version: program_version | | Build: build_date | | Author: program_author | +--------------------------------------+ Args: ``program_name`` -- ``str`` The name of the program. ``program_version`` -- ``str`` The version of the program. ``build_date`` -- ``str`` The build date of the program. ``program_author`` -- ``str`` The author of the program. ``line_width`` -- ``int`` The width to make the header (default = 80). Returns: ``str`` A formatted header for the program which can be printed. If ``program_version``, ``build_date``, or ``program_author`` are not included, the line containing them will not be in the result. """ # Do some set up line_break = '-' * line_width center_str = '|{:^%d}|\n' % (line_width - 2) corner_str = '+%s+\n' % (line_break[:-2]) # Build the header string result = corner_str result += center_str.format(program_name) result += corner_str if program_version is not None: result += center_str.format('Version: %s' % program_version) if build_date is not None: result += center_str.format('Build: %s' % build_date) if program_author is not None: result += center_str.format('Author: %s' % program_author) result += corner_str + '\n' # Give it back return result # if __name__ == '__main__': # printer = SLPrinter('Testing', maxlen=50) # sys.stdout = printer # print('This is a test to see if this will work the way it should and cut the text off 50 characters in') # print('111111111111111111112222222222222222222244444444444444444444') # print('Hi') # # printer.write_tabbed('This is a test to make sure tabbing works the way it should while breaking lines!', 1)