def test_new_line(self): stream = StringIO() console = Console(stream) console.new_line('The quick brown fox jumps over the lazy dog') output = stream.getvalue() self.assertEqual( repr(output), repr( '\r' 'The quick brown fox jumps over the lazy dog' + ' ' * 37 + '\n' ) )
class BaseSyncOperation(object): """Base class for website dump / load operations""" status_colors = { SyncStatus.DELETED: 'MAGENTA', SyncStatus.ADDED: 'GREEN', SyncStatus.MODIFIED: 'CYAN', } def __init__(self, root_url=None, error_class=None, stdout=None, stderr=None, get_input=None, **options): self.root_url = root_url or '/' self.error_class = error_class or RuntimeError self.stdout = stdout or sys.stdout self.stderr = stderr or sys.stderr self.dry_run = options['dry_run'] self.no_interactive = options['no_interactive'] self.quiet = options['quiet'] self.force = options['force'] self.git_add = options['git_add'] self.no_color = options.get('no_color') self.console = Console(self.stdout) self.get_input = get_input or default_get_input def status_fg_color(self, status): """Determines fg color for status""" if self.no_color: fg_color = None else: try: base_status, extra_status = status except ValueError: base_status, extra_status = status, None if extra_status == SyncStatus.SKIPPED: fg_color = None else: fg_color = self.status_colors.get(base_status, None) return fg_color def log(self, message): """Writes informative line of text to stdout""" if not self.quiet: self.console.new_line(message) def log_status(self, status, item): """Writes informative line about status of given item (in color!)""" if not self.quiet: message = '{0} {1}'.format(status, item) self.console.new_line(message, fg_color=self.status_fg_color(status)) def summary(self, summary_dict): """Writes summary line of text to stdout""" self.console.new_line('SUMMARY:') if summary_dict: for status, occurrences in summary_dict.items(): message = '\t{0} [{1}] = {2}'.format( SyncStatus.describe(status), status, occurrences) self.console.new_line(message, fg_color=self.status_fg_color(status)) else: self.console.new_line('\tNo changes!') def error(self, message): """Stops processing by raising an exception""" raise self.error_class(message) def confirm(self, *args, **kwargs): """Asks user for confirmation, providing boolean output""" if self.no_interactive: choice = True else: # Delimiter: self.console.new_line( '{s} CONFIRMATION REQUIRED: {s}'.format(s='#' * 28)) # Show diff if available: diff = kwargs.get('diff') if diff: self.console.new_line('{s} DIFF {s}'.format(s='-' * 37)) for diff_line in diff.splitlines(): if diff_line.startswith('+'): fg_color = 'GREEN' elif diff_line.startswith('-'): fg_color = 'RED' else: fg_color = None self.console.new_line(diff_line, fg_color=fg_color) self.console.new_line('{s}'.format(s='-' * 80)) # Show other messages: message = kwargs.get('message') messages = [message] if message else list(args) messages.append( kwargs.get('question', 'Do you want to apply this change?')) for message in messages: self.console.new_line(message) choice = None while choice is None: user_text = self.get_input('Y/N? ') if user_text in ('Y', 'y'): choice = True elif user_text in ('N', 'n'): choice = False # Delimiter: self.console.new_line('{s}'.format(s='#' * 80)) return choice def run(self): """Performs the operation""" raise NotImplementedError