def __init__(self, message, autotick, detail_message_callback, tick_delay, interruptable, aborted_message, spinner_override_message): self._stream = sys.stderr if message is None: self._spinner_only = True self._message = '' self._prefix = '' else: self._spinner_only = False self._message = message self._prefix = message + '...' self._detail_message_callback = detail_message_callback self.spinner_override_message = spinner_override_message self._ticks = 0 self._done = False self._lock = threading.Lock() self._tick_delay = tick_delay self._ticker = None console_width = console_attr.ConsoleAttr().GetTermSize()[0] if console_width < 0: # This can happen if we're on a pseudo-TTY. Set it to 0 and also # turn off output to prevent hanging. console_width = 0 self._output_enabled = log.IsUserOutputEnabled() and console_width != 0 # Don't bother autoticking if we aren't going to print anything. self.__autotick = autotick and self._output_enabled self._interruptable = interruptable self._aborted_message = aborted_message self._old_signal_handler = None self._symbols = console_attr.GetConsoleAttr().GetProgressTrackerSymbols()
def __init__(self, message, stages, success_message, failure_message, autotick, tick_delay, interruptable, aborted_message, tracker_id, done_message_callback): super(_BaseStagedProgressTracker, self).__init__(stages) self._stream = sys.stderr # TODO(b/111637901): Support detailed message callback when true multiline # support is available. self._message = message self._success_message = success_message self._failure_message = failure_message self._aborted_message = aborted_message self._done_message_callback = done_message_callback self._tracker_id = tracker_id console_width = console_attr.ConsoleAttr().GetTermSize()[0] if console_width < 0: # This can happen if we're on a pseudo-TTY. Set it to 0 and also # turn off output to prevent hanging. console_width = 0 self._output_enabled = log.IsUserOutputEnabled() and console_width != 0 # Don't bother autoticking if we aren't going to print anything. self.__autotick = autotick and self._output_enabled self._interruptable = interruptable self._tick_delay = tick_delay self._symbols = console_attr.GetConsoleAttr( ).GetProgressTrackerSymbols() self._done = False self._ticks = 0 self._ticker = None self._stage_being_displayed = None self._running_stages_queue = [] self._completed_stages = [] self._lock = threading.Lock()
def __init__(self, *args, **kwargs): super(LinterRenderer, self).__init__(*args, **kwargs) # disable use of ANSI escape sequences while linting self._attr = console_attr.ConsoleAttr(encoding="ascii") self._bullet = self._attr.GetBullets() self._csi_char = None self._file_out = self._out # the output file inherited from TextRenderer self._null_out = io.StringIO() self._buffer = io.StringIO() self._out = self._buffer self._analyze = { "NAME": self._analyze_name, "EXAMPLES": self._analyze_examples, "DESCRIPTION": self._analyze_description } self._heading = "" self._prev_heading = "" self.example = False self.command_name = "" self.name_section = "" self.command_name_length = 0 self.command_text = "" self.equals_violation_flags = [] self.nonexistent_violation_flags = [] self.json_object = collections.OrderedDict()
def __init__(self, prompt, choices=None, out=None, width=None, height=None, pad=' '): """Constructor. Args: prompt: str or None, The prompt string. choices: callable or list, A callable with no arguments that returns the list of all choices, or the list of choices. out: stream, The output stream, sys.stderr by default. width: int, The total display width in characters. height: int, The total display height in lines. pad: str, String inserted before each column. """ self._prompt = prompt self._choices = choices self._out = out or sys.stderr self._attr = console_attr.ConsoleAttr() term_width, term_height = self._attr.GetTermSize() if width is None: width = 80 if width > term_width: width = term_width self._width = width if height is None: height = 40 if height > term_height: height = term_height self._height = height self._pad = pad
def __init__(self, *args, **kwargs): super(LinterRenderer, self).__init__(*args, **kwargs) # disable use of ANSI escape sequences while linting self._attr = console_attr.ConsoleAttr(encoding='ascii') self._bullet = self._attr.GetBullets() self._csi_char = None self._file_out = self._out # the output file inherited from TextRenderer self._null_out = io.StringIO() self._buffer = io.StringIO() self._out = self._buffer self._analyze = { 'NAME': self._analyze_name, 'EXAMPLES': self._analyze_examples, 'DESCRIPTION': self._analyze_description, 'POSITIONAL ARGUMENTS': self._analyze_argument_sections, 'REQUIRED FLAGS': self._analyze_argument_sections, 'OPTIONAL FLAGS': self._analyze_argument_sections, 'FLAGS': self._analyze_argument_sections, 'LIST COMMAND FLAGS': self._analyze_argument_sections } self._heading = '' self._prev_heading = '' self._example_errors = False self._has_example_section = False self.example = False self.command_name = '' self.name_section = '' self.command_name_length = 0 self.command_text = '' self.equals_violation_flags = [] self.nonexistent_violation_flags = [] self.findings = collections.OrderedDict()
def __init__(self, message, stream, suffix='', detail_message_callback=None, indentation_level=0): """Constructor. Args: message: str, the message that this object represents. stream: The output stream to write to. suffix: str, The suffix that will be appended to the very end of the message. detail_message_callback: func() -> str, A no argument function that will be called and the result will be added after the message and before the suffix on every call to Print(). indentation_level: int, The indentation level of the message. Each indentation is represented by two spaces. """ self._stream = stream self._message = message self._suffix = suffix # TODO(b/111592003): May be better to get this on demand. self._console_width = console_attr.ConsoleAttr().GetTermSize()[0] if self._console_width < 0: self._console_width = 0 self._detail_message_callback = detail_message_callback self._level = indentation_level # Private attributes used for printing. self._no_output = False if (self._console_width - (INDENTATION_WIDTH * indentation_level)) <= 0: # The indentation won't fit into the width of the console. In this case # just don't output. This should be rare and better than failing the # command. self._no_output = True self._num_lines = 0 self._lines = [] self._has_printed = False
def __init__(self, label, stream=log.status, total_ticks=60, first=True, last=True): """Creates a progress bar for the given action. Args: label: str, The action that is being performed. stream: The output stream to write to, stderr by default. total_ticks: int, The number of ticks wide to make the progress bar. first: bool, True if this is the first bar in a stacked group. last: bool, True if this is the last bar in a stacked group. """ self._stream = stream self._ticks_written = 0 self._total_ticks = total_ticks self._first = first self._last = last attr = console_attr.ConsoleAttr() self._box = attr.GetBoxLineCharacters() self._redraw = (self._box.d_dr != self._box.d_vr or self._box.d_dl != self._box.d_vl) max_label_width = self._total_ticks - 4 if len(label) > max_label_width: label = label[:max_label_width - 3] + '...' elif len(label) < max_label_width: diff = max_label_width - len(label) label += ' ' * diff left = self._box.d_vr + self._box.d_h right = self._box.d_h + self._box.d_vl self._label = u'{left} {label} {right}'.format(left=left, label=label, right=right)
def __init__(self, message, stream, indentation_level=0): """Constructor. Args: message: str, the message that this object represents. stream: The output stream to write to. indentation_level: int, The indentation level of the message. Each indentation is represented by two spaces. """ self._stream = stream # Some terminals will move the cursor to the next line once console_width # characters have been written. So for now we need to use 1 less than the # actual console width to prevent automatic wrapping leading to improper # text formatting. self._console_width = console_attr.ConsoleAttr().GetTermSize()[0] - 1 if self._console_width < 0: self._console_width = 0 self._level = indentation_level # Private attributes used for printing. self._no_output = False if (self._console_width - (INDENTATION_WIDTH * indentation_level)) <= 0: # The indentation won't fit into the width of the console. In this case # just don't output. This should be rare and better than failing the # command. self._no_output = True self._message = None self._lines = [] self._has_update = False self._num_lines_changed = False self._UpdateMessage(message)
def _ProcessColumn(self, index, row): """Processes a single column value when computing column widths.""" record = row[index] last_index = len(row) - 1 if isinstance(record, _Marker): if index == last_index: self._MergeColumnWidths( record.CalculateColumnWidths( self._max_column_width, self._indent_length + INDENT_STEP)) return else: raise TypeError('Markers can only be used in the last column.') if _IsLastColumnInRow(row, index, last_index, self._skip_empty): self._SetWidth(index, 0) else: console_attr = self._console_attr if self._console_attr is None: console_attr = ca.ConsoleAttr() width = console_attr.DisplayWidth( str(record)) + self._separator_width if index == 0: width += self._indent_length self._SetWidth(index, width)
def __init__(self, stages, interruptable=False, aborted_message=''): super(NoOpStagedProgressTracker, self).__init__( message='', stages=stages, success_message='', failure_message='', autotick=False, tick_delay=0, interruptable=interruptable, aborted_message='', tracker_id='', done_message_callback=None, console=console_attr.ConsoleAttr( encoding='ascii', suppress_output=True)) self._aborted_message = aborted_message self._done = False
def __init__(self, message, stream, suffix='', detail_message_callback=None, indentation_level=0): """Constructor. Args: message: str, the message that this object represents. stream: The output stream to write to. suffix: str, The suffix that will be appended to the very end of the message. detail_message_callback: func() -> str, A no argument function that will be called and the result will be added after the message and before the suffix on every call to Print(). indentation_level: int, The indentation level of the message. Each indentation is represented by two spaces. """ self._stream = stream self._message = message self._suffix = suffix # TODO(b/111592003): May be better to get this on demand. # TODO(b/112460253): On terminals that don't automatically line wrap, use # the entire console width. # Some terminals will move the cursor to the next line once console_width # characters have been written. So for now we need to use 1 less than the # actual console width to prevent automatic wrapping leading to improper # text formatting. self._console_width = console_attr.ConsoleAttr().GetTermSize()[0] - 1 if self._console_width < 0: self._console_width = 0 self._detail_message_callback = detail_message_callback self._level = indentation_level # Private attributes used for printing. self._no_output = False if (self._console_width - (INDENTATION_WIDTH * indentation_level)) <= 0: # The indentation won't fit into the width of the console. In this case # just don't output. This should be rare and better than failing the # command. self._no_output = True self._num_lines = 0 self._lines = [] self._has_printed = False
def _GenerateColumnValue(self, index, row, indent_length, column_widths): """Generates the string value for one column in one row.""" width = column_widths.widths[index] if index == 0: width -= indent_length separator = self.separator if index < len(row) - 1 else '' console_attr = self._console_attr if self._console_attr is None: console_attr = ca.ConsoleAttr() # calculate number of ' ' paddings required. # avoid using '{: <10}'.format() which doesn't calculate the displaylength # of ANSI ecoded sequence correctly. n_padding = (width - console_attr.DisplayWidth(str(row[index])) - len(separator)) return str(row[index]) + separator + (n_padding * ' ')
def __init__(self, label, stream=log.status, total_ticks=60, first=True, last=True): """Creates a progress bar for the given action. Args: label: str, The action that is being performed. stream: The output stream to write to, stderr by default. total_ticks: int, The number of ticks wide to make the progress bar. first: bool, True if this is the first bar in a stacked group. last: bool, True if this is the last bar in a stacked group. """ self._stream = stream self._ticks_written = 0 self._total_ticks = total_ticks self._first = first self._last = last attr = console_attr.ConsoleAttr(out=stream) self._box = attr.GetBoxLineCharacters() self._redraw = (self._box.d_dr != self._box.d_vr or self._box.d_dl != self._box.d_vl) # If not interactive, we can't use carriage returns, so treat every progress # bar like it is independent, do not try to merge and redraw them. We only # need to do this if it was going to attempt to redraw them in the first # place (there is no redraw if the character set does not support different # characters for the corners). if self._redraw and not IsInteractive(error=True): self._first = True self._last = True max_label_width = self._total_ticks - 4 if len(label) > max_label_width: label = label[:max_label_width - 3] + '...' elif len(label) < max_label_width: diff = max_label_width - len(label) label += ' ' * diff left = self._box.d_vr + self._box.d_h right = self._box.d_h + self._box.d_vl self._label = u'{left} {label} {right}'.format(left=left, label=label, right=right)
def StagedProgressTracker( message, stages, tracker_id=None, autotick=True, tick_delay=0.1, interruptable=True, done_message_callback=None, success_message=None, warning_message=None, failure_message=None, aborted_message=console_io.OperationCancelledError.DEFAULT_MESSAGE, suppress_output=False): """A progress tracker for performing actions with multiple stages. The progress tracker is a context manager. To start displaying information about a running stage, call StartStage within the staged progress tracker context. To update the message of a stage, use UpdateStage. When a stage is completed/failed there are CompleteStage and FailStage methods on the tracker as well. Note that stages do not need to be started/completed in order. In non-multiline (the only supported mode) output mode, the displayed stage will be the earliest started stage that has not been completed. Example Usage: stages = [ Stage('Getting bread...', key='bread'), Stage('Getting peanut butter...', key='pb'), Stage('Making sandwich...', key='make')] with StagedProgressTracker( 'Making sandwich...', stages, success_message='Time to eat!', failure_message='Time to order delivery..!', tracker_id='meta.make_sandwich') as tracker: tracker.StartStage('bread') # Go to pantry tracker.UpdateStage('bread', 'Looking for bread in the pantry') # Get bread tracker.CompleteStage('bread', 'Got some whole wheat bread!') tracker.StartStage('pb') # Look for peanut butter if pb_not_found: error = exceptions.NoPeanutButterError('So sad!') tracker.FailStage('pb', error) elif pb_not_organic: tracker.CompleteStageWithWarning('pb', 'The pb is not organic!') else: tracker.CompleteStage('bread', 'Got some organic pb!') Args: message: str, The message to show next to the spinner. stages: list[Stage], A list of stages for the progress tracker to run. Once you pass the stages to a StagedProgressTracker, they're owned by the tracker and you should not mutate them. tracker_id: str The ID of this tracker that will be used for metrics. autotick: bool, True to have the spinner tick on its own. Otherwise, you need to call Tick() explicitly to move the spinner. tick_delay: float, The amount of time to wait between ticks, in second. interruptable: boolean, True if the user can ctrl-c the operation. If so, it will stop and will report as aborted. If False, done_message_callback: func, A callback to get a more detailed done message. success_message: str, A message to display on success of all tasks. warning_message: str, A message to display when no task fails but one or more tasks complete with a warning and none fail. failure_message: str, A message to display on failure of a task. aborted_message: str, A custom message to put in the exception when it is cancelled by the user. suppress_output: bool, True to suppress output from the tracker. Returns: The progress tracker. """ style = properties.VALUES.core.interactive_ux_style.Get() if (suppress_output or style == properties.VALUES.core.InteractiveUXStyles.OFF.name): return NoOpStagedProgressTracker(stages, interruptable, aborted_message) elif style == properties.VALUES.core.InteractiveUXStyles.TESTING.name: return _StubStagedProgressTracker( message, stages, interruptable, aborted_message) else: is_tty = console_io.IsInteractive(error=True) if is_tty: if console_attr.ConsoleAttr().SupportsAnsi(): tracker_cls = _MultilineStagedProgressTracker else: tracker_cls = _NormalStagedProgressTracker else: tracker_cls = _NonInteractiveStagedProgressTracker return tracker_cls( message, stages, success_message, warning_message, failure_message, autotick, tick_delay, interruptable, aborted_message, tracker_id, done_message_callback)
def _Print(self, message=''): """Reprints the prefix followed by an optional message. If there is a multiline message, we print the full message and every time the Prefix Message is the same, we only reprint the last line to account for a different 'message'. If there is a new message, we print on a new line. Args: message: str, suffix of message """ display_message = self._GetPrefix() # If we are not in a tty, _Print() is called exactly twice. The first time # it should print the prefix, the last time it should print just the 'done' # message since we are not using any escape characters at all. if not self._is_tty: sys.stderr.write(message or display_message + '\n') return console_width = console_attr.ConsoleAttr().GetTermSize()[0] - 1 if console_width < 0: console_width = 0 # The whole message will fit in the current console width and the previous # line was not a multiline display so we can overwrite. # If the previous and current messages are same we have multiline display # so we only need the portion of text to rewrite on current line. if ((len(display_message + message) <= console_width and not self._multi_line) or display_message == self._last_display_message): self._last_display_message = display_message start_place = len(display_message) - (len(display_message) % console_width) if display_message: display_message += message # If size of the message is a multiple of the console_width, this will # cause start place to begin at len(display_message) so we index the # message from the end. if start_place == 0: start_place = -(console_width + len(message)) current_message = display_message[start_place:] # We clear the current display and reprint the last line. sys.stderr.write('\r' + console_width * ' ') sys.stderr.write('\r' + current_message) elif not console_width: # This can happen if we're on a pseudo-TTY; ignore this to prevent # hanging. pass else: # If we have to do multiline display or a new message. # If we have written something to the console before the new message, # cursor will be at the end of the line so we need to go to the next line. # If we are printing for the first time, the cursor will already be at # a new line. sys.stderr.write('\n' if self._last_display_message else '') self._last_display_message = display_message display_message += message # We may or may not use multiline display while display_message: current_printing_message = display_message[:console_width] display_message = display_message[console_width:] # We print a new line if there is more to print in display_message. sys.stderr.write(current_printing_message + ('\n' if display_message else '')) # If there is still more to print, we will be using multiline display # for future printing. We will not want to erase the last line # if a new line is able to fit in the whole console (first if). # If self.multi_line was already True, we do not want to make it # False self._multi_line = (True if display_message or self._multi_line else False) sys.stderr.flush()