Beispiel #1
0
 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()
Beispiel #3
0
    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
Beispiel #5
0
    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()
Beispiel #6
0
  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
Beispiel #7
0
  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)
Beispiel #8
0
  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)
Beispiel #9
0
    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
Beispiel #11
0
    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
Beispiel #12
0
    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)
Beispiel #14
0
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()