Exemple #1
0
    def __init__(self, encoding=None, out=None):
        """Constructor.

    Args:
      encoding: Encoding override.
        ascii -- ASCII art. This is the default.
        utf8 -- UTF-8 unicode.
        win -- Windows code page 437.
      out: The console output file stream, log.out if None.
    """
        self._out = out or log.out
        # Normalize the encoding name.
        if not encoding:
            encoding = 'ascii'
            if hasattr(self._out, 'encoding') and self._out.encoding:
                console_encoding = self._out.encoding.lower()
                if 'utf-8' in console_encoding:
                    encoding = 'utf8'
                elif 'cp437' in console_encoding:
                    encoding = 'win'
        self._encoding = encoding
        self._term = os.getenv('TERM', '').lower()

        # ANSI "standard" attributes.
        if self._encoding != 'ascii' and ('screen' in self._term
                                          or 'xterm' in self._term):
            # Select Graphic Rendition paramaters from
            # http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
            # Italic '3' would be nice here but its not widely supported.
            self._csi = '\x1b['
            self._font_bold = '1'
            self._font_italic = '4'
        else:
            self._csi = None
            self._font_bold = ''
            self._font_italic = ''

        # Encoded character attributes.
        if self._encoding == 'utf8':
            self._box_line_characters = BoxLineCharactersUtf8()
            self._bullets = self._BULLETS_UTF8
        elif self._encoding == 'win':
            self._box_line_characters = BoxLineCharactersWindows()
            self._bullets = self._BULLETS_WINDOWS
        else:
            self._box_line_characters = BoxLineCharactersAscii()
            self._bullets = self._BULLETS_ASCII

        # OS specific attributes.
        self._get_raw_char = [console_attr_os.GetRawCharFunction()]
        self._term_size = console_attr_os.GetTermSize()
Exemple #2
0
    def __init__(self, encoding=None):
        """Constructor.

    Args:
      encoding: Encoding override.
        ascii -- ASCII art. This is the default.
        utf8 -- UTF-8 unicode.
        win -- Windows code page 437.
    """
        # Normalize the encoding name.
        if not encoding:
            encoding = self._GetConsoleEncoding()
        elif encoding == 'win':
            encoding = 'cp437'
        self._encoding = encoding or 'ascii'
        self._term = os.getenv('TERM', '').lower()

        # ANSI "standard" attributes.
        if self._encoding != 'ascii' and ('screen' in self._term
                                          or 'xterm' in self._term):
            # Select Graphic Rendition paramaters from
            # http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
            # Italic '3' would be nice here but its not widely supported.
            self._csi = '\x1b['
            self._font_bold = '1'
            self._font_italic = '4'
        else:
            self._csi = None
            self._font_bold = ''
            self._font_italic = ''

        # Encoded character attributes.
        if self._encoding == 'utf8':
            self._box_line_characters = BoxLineCharactersUnicode()
            self._bullets = self._BULLETS_UNICODE
            self._progress_tracker_symbols = ProgressTrackerSymbolsUnicode()
        elif self._encoding == 'cp437':
            self._box_line_characters = BoxLineCharactersUnicode()
            self._bullets = self._BULLETS_WINDOWS
            # Windows does not suport the unicode characters used for the spinner.
            self._progress_tracker_symbols = ProgressTrackerSymbolsAscii()
        else:
            self._box_line_characters = BoxLineCharactersAscii()
            self._bullets = self._BULLETS_ASCII
            self._progress_tracker_symbols = ProgressTrackerSymbolsAscii()

        # OS specific attributes.
        self._get_raw_key = [console_attr_os.GetRawKeyFunction()]
        self._term_size = console_attr_os.GetTermSize()

        self._display_width_cache = {}
def GetDivider(text=''):
    """Return a console-width divider (ex: '======================' (etc.)).

  Supports messages (ex: '=== Messsage Here ===').

  Args:
    text: str, a message to display centered in the divider.

  Returns:
    str, the formatted divider
  """
    if text:
        text = ' ' + text + ' '
    width, _ = console_attr_os.GetTermSize()
    return text.center(width, '=')
Exemple #4
0
def _FormatNonAsciiMarkerString(args):
    r"""Format a string that will mark the first non-ASCII character it contains.


  Example:

  >>> args = ['command.py', '--foo=\xce\x94']
  >>> _FormatNonAsciiMarkerString(args) == (
  ...     'command.py --foo=\u0394\n'
  ...     '                 ^ invalid character'
  ... )
  True

  Args:
    args: The arg list for the command executed

  Returns:
    unicode, a properly formatted string with two lines, the second of which
      indicates the non-ASCII character in the first.

  Raises:
    ValueError: if the given string is all ASCII characters
  """
    # pos is the position of the first non-ASCII character in ' '.join(args)
    pos = 0
    for arg in args:
        first_non_ascii_index = _NonAsciiIndex(arg)
        if first_non_ascii_index >= 0:
            pos += first_non_ascii_index
            break
        # this arg was all ASCII; add 1 for the ' ' between args
        pos += len(arg) + 1
    else:
        raise ValueError(
            'The command line is composed entirely of ASCII characters.')

    # Make a string that, when printed in parallel, will point to the non-ASCII
    # character
    marker_string = ' ' * pos + _MARKER

    # Make sure that this will still print out nicely on an odd-sized screen
    align = len(marker_string)
    args_string = ' '.join([console_attr.SafeText(arg) for arg in args])
    width, _ = console_attr_os.GetTermSize()
    fill = '...'
    if width < len(_MARKER) + len(fill):
        # It's hopeless to try to wrap this and make it look nice. Preserve it in
        # full for logs and so on.
        return '\n'.join((args_string, marker_string))
    # If len(args_string) < width < len(marker_string) (ex:)
    #
    #   args_string   = 'command BAD'
    #   marker_string = '        ^ invalid character'
    #   width     = len('----------------')
    #
    # then the truncation can give a result like the following:
    #
    #   args_string   = 'command BAD'
    #   marker_string = '   ^ invalid character'
    #
    # (This occurs when args_string is short enough to not be truncated, but
    # marker_string is long enough to be truncated.)
    #
    # ljust args_string to make it as long as marker_string before passing to
    # _TruncateToLineWidth, which will yield compatible truncations. rstrip at the
    # end to get rid of the new trailing spaces.
    formatted_args_string = _TruncateToLineWidth(args_string.ljust(align),
                                                 align,
                                                 width,
                                                 fill=fill).rstrip()
    formatted_marker_string = _TruncateToLineWidth(marker_string, align, width)
    return '\n'.join((formatted_args_string, formatted_marker_string))
Exemple #5
0
    def Build(self, docker_client):
        """Calls "docker build" command.

    Args:
      docker_client: instance of docker.Client connected to a Docker daemon.

    Raises:
      ImageBuildError: if the image could not be built.
    """
        log.info('Building docker image %s from %s/Dockerfile:', self.tag,
                 self._dockerfile_dir)

        width, _ = console_attr_os.GetTermSize()
        log.status.Print(
            DOCKER_OUTPUT_BEGIN.center(width, DOCKER_OUTPUT_LINE_CHAR))

        build_res = docker_client.build(path=self._dockerfile_dir,
                                        tag=self.tag,
                                        quiet=False,
                                        fileobj=None,
                                        nocache=self._nocache,
                                        rm=self._rm,
                                        pull=False)

        info = None
        error = None
        error_detail = None
        log_records = []
        try:
            for line in build_res:
                line = line.strip()
                if not line:
                    continue
                log_record = json.loads(line)
                log_records.append(log_record)
                if 'stream' in log_record:
                    info = log_record['stream'].strip()
                    log.status.Print(info)
                if 'error' in log_record:
                    error = log_record['error'].strip()
                    # will be logged to log.error in the thrown exception
                    log.status.Print(error)
                if 'errorDetail' in log_record:
                    error_detail = log_record['errorDetail']['message'].strip()
                    log.status.Print(error_detail)
        except docker.errors.APIError as e:
            log.error(e.explanation)
            error = e.explanation
            error_detail = ''
        finally:
            log.status.Print(DOCKER_OUTPUT_LINE_CHAR * width + '\n')

        if not log_records:
            raise ImageBuildError(
                'Error building docker image {0} [with no output]'.format(
                    self.tag))

        success_message = log_records[-1].get(_STREAM)
        if success_message:
            m = _SUCCESSFUL_BUILD_PATTERN.match(success_message)
            if m:
                # The build was successful.
                self._id = m.group(1)
                log.info('Image %s built, id = %s', self.tag, self.id)
                return

        raise ImageBuildError('Docker build aborted: ' + error)
Exemple #6
0
 def _PrintLastLine(self, msg=''):
   """Print a pretty ending line to identify end of build output logs."""
   width, _ = console_attr_os.GetTermSize()
   # We print an extra blank visually separating the log from other output.
   self._PrintLogLine(msg.center(width, self.OUTPUT_LINE_CHAR) + '\n')
Exemple #7
0
 def _PrintFirstLine(self):
   """Print a pretty starting line to identify start of build output logs."""
   width, _ = console_attr_os.GetTermSize()
   self._PrintLogLine(
       self.LOG_OUTPUT_BEGIN.center(width, self.OUTPUT_LINE_CHAR))
    def __init__(self, encoding=None, term=None, suppress_output=False):
        """Constructor.

    Args:
      encoding: Encoding override.
        ascii -- ASCII art. This is the default.
        utf8 -- UTF-8 unicode.
        win -- Windows code page 437.
      term: Terminal override. Replaces the value of ENV['TERM'].
      suppress_output: True to create a ConsoleAttr that doesn't want to output
        anything.
    """
        # Normalize the encoding name.
        if not encoding:
            encoding = self._GetConsoleEncoding()
        elif encoding == 'win':
            encoding = 'cp437'
        self._encoding = encoding or 'ascii'
        if suppress_output:
            self._term = ''
        elif term:
            self._term = term
        else:
            self._term = encoding_util.GetEncodedValue(os.environ, 'TERM',
                                                       '').lower()

        # ANSI "standard" attributes.
        if self.SupportsAnsi():
            # Select Graphic Rendition paramaters from
            # http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
            # Italic '3' would be nice here but its not widely supported.
            self._csi = '\x1b['
            self._font_bold = '1'
            self._font_italic = '4'
        else:
            self._csi = None
            self._font_bold = ''
            self._font_italic = ''

        # Encoded character attributes.
        is_screen_reader = properties.VALUES.accessibility.screen_reader.GetBool(
        )
        if self._encoding == 'utf-8' and not is_screen_reader:
            self._box_line_characters = BoxLineCharactersUnicode()
            self._bullets = self._BULLETS_UNICODE
            self._progress_tracker_symbols = ProgressTrackerSymbolsUnicode()
        elif self._encoding == 'cp437' and not is_screen_reader:
            self._box_line_characters = BoxLineCharactersUnicode()
            self._bullets = self._BULLETS_WINDOWS
            # Windows does not suport the unicode characters used for the spinner.
            self._progress_tracker_symbols = ProgressTrackerSymbolsAscii()
        else:
            self._box_line_characters = BoxLineCharactersAscii()
            if is_screen_reader:
                self._box_line_characters = BoxLineCharactersScreenReader()
            self._bullets = self._BULLETS_ASCII
            self._progress_tracker_symbols = ProgressTrackerSymbolsAscii()

        # OS specific attributes.
        self._get_raw_key = [console_attr_os.GetRawKeyFunction()]
        self._term_size = ((0, 0) if suppress_output else
                           console_attr_os.GetTermSize())

        self._display_width_cache = {}
Exemple #9
0
 def testGetTermsizeTput(self):
     columns, lines = console_attr_os.GetTermSize()
     self.assertEqual((84, 28), (columns, lines))
Exemple #10
0
 def testGetTermsizeEnvironment(self):
     columns, lines = console_attr_os.GetTermSize()
     self.assertEqual((83, 27), (columns, lines))
Exemple #11
0
 def testGetTermsizeWindowsNoScreenBuffer(self):
     self.imports.SetImport('ctypes', self.MockCtypesModuleNoScreenBuffer)
     columns, lines = console_attr_os.GetTermSize()
     self.assertEqual((80, 24), (columns, lines))
Exemple #12
0
 def testGetTermsizePosix(self):
     columns, lines = console_attr_os.GetTermSize()
     self.assertEqual((81, 25), (columns, lines))
Exemple #13
0
def _FormatNonAsciiMarkerString(args_string):
    u"""Format a string that will mark the first non-ASCII character it contains.


  Example:

  >>> args = 'command.py --foo=\xce\x94'
  >>> _FormatNonAsciiMarkerString(args) == (
  ...     'command.py --foo=\u0394\n'
  ...     '                 ^ invalid character'
  ... )
  True

  Args:
    args_string: str representing the command executed

  Returns:
    unicode, a properly formatted string with two lines, the second of which
      indicates the non-ASCII character in the first.

  Raises:
    ValueError: if the given string is all ASCII characters
  """
    # idx is the first index of the first non-ASCII character in args_string
    idx = None
    for idx, char in enumerate(args_string):
        try:
            char.decode('ascii')
            # idx gets set by enumerate; unset it to indicate that the last character
            # was successfully decoded as ASCII
            idx = None
        except UnicodeError:
            # idx will remain set, indicating the first non-ASCII character
            break
    if idx is None:
        raise ValueError(
            'Given string is composed entirely of ASCII characters.')

    # Make a string that, when printed in parallel, will point to the non-ASCII
    # character
    marker_string = ' ' * idx + _MARKER

    # Make sure that this will still print out nicely on an odd-sized screen
    align = len(marker_string)
    args_string = args_string.decode('utf-8', 'replace')
    width, _ = console_attr_os.GetTermSize()
    fill = '...'
    if width < len(_MARKER) + len(fill):
        # It's hopeless to try to wrap this and make it look nice. Preserve it in
        # full for logs and so on.
        return '\n'.join((args_string, marker_string))
    # If len(args_string) < width < len(marker_string) (ex:)
    #
    #   args_string   = 'command BAD'
    #   marker_string = '        ^ invalid character'
    #   width     = len('----------------')
    #
    # then the truncation can give a result like the following:
    #
    #   args_string   = 'command BAD'
    #   marker_string = '   ^ invalid character'
    #
    # (This occurs when args_string is short enough to not be truncated, but
    # marker_string is long enough to be truncated.)
    #
    # ljust args_string to make it as long as marker_string before passing to
    # _TruncateToLineWidth, which will yield compatible truncations. rstrip at the
    # end to get rid of the new trailing spaces.
    formatted_args_string = _TruncateToLineWidth(args_string.ljust(align),
                                                 align,
                                                 width,
                                                 fill=fill).rstrip()
    formatted_marker_string = _TruncateToLineWidth(marker_string, align, width)
    return '\n'.join((formatted_args_string, formatted_marker_string))
Exemple #14
0
 def _PrintFirstLine(self):
   width, _ = console_attr_os.GetTermSize()
   self._PrintLogLine(
       self.LOG_OUTPUT_BEGIN.center(width, self.OUTPUT_LINE_CHAR))
Exemple #15
0
 def testGetTermsizeFallBack(self):
     columns, lines = console_attr_os.GetTermSize()
     self.assertEqual((80, 24), (columns, lines))
Exemple #16
0
 def _PrintLastLine(self, msg=''):
   width, _ = console_attr_os.GetTermSize()
   # We print an extra blank visually separating the log from other output.
   self._PrintLogLine(msg.center(width, self.OUTPUT_LINE_CHAR) + '\n')
Exemple #17
0
    def Execute(self, args=None, call_arg_complete=True):
        """Execute the CLI tool with the given arguments.

    Args:
      args: [str], The arguments from the command line or None to use sys.argv
      call_arg_complete: Call the _ArgComplete function if True

    Returns:
      The result of executing the command determined by the command
      implementation.

    Raises:
      ValueError: for ill-typed arguments.
    """
        if isinstance(args, basestring):
            raise ValueError(
                'Execute expects an iterable of strings, not a string.')

        # The argparse module does not handle unicode args when run in Python 2
        # because it uses str(x) even when type(x) is unicode. This sets itself up
        # for failure because it converts unicode strings back to byte strings which
        # will trigger ASCII codec exceptions. It works in Python 3 because str() is
        # equivalent to unicode() in Python 3. The next Pythonically magic and dirty
        # statement coaxes the Python 3 behavior out of argparse running in
        # Python 2. Doing it here ensures that the workaround is in place for
        # calliope argparse use cases.
        argparse.str = unicode

        if call_arg_complete:
            _ArgComplete(self.__top_element.ai)

        if not args:
            args = sys.argv[1:]

        # help ... is the same as ... --help or ... --document=style=help. We use
        # --document=style=help to signal the metrics.Help() 'help' label in
        # actions.RenderDocumentAction().Action(). It doesn't matter if we append
        # to a command that already has --help or --document=style=... because the
        # first --help/--document from the left takes effect. Note that
        # `help ... --help` produces help for the help command itself.
        if args and args[0] == 'help' and '--help' not in args:
            args = args[1:] + ['--document=style=help']

        # Look for a --configuration flag and update property state based on
        # that before proceeding to the main argparse parse step.
        named_configs.FLAG_OVERRIDE_STACK.PushFromArgs(args)
        properties.VALUES.PushInvocationValues()

        # Set the command name in case an exception happens before the command name
        # is finished parsing.
        command_path_string = self.__name
        specified_arg_names = None

        argv = self._ConvertNonAsciiArgsToUnicode(args)
        old_user_output_enabled = None
        old_verbosity = None
        try:
            args = self.__parser.parse_args(argv)
            command_path_string = '.'.join(args.command_path)
            if not args.calliope_command.IsUnicodeSupported():
                self._EnforceAsciiArgs(argv)
            specified_arg_names = args.GetSpecifiedArgNames()

            # -h|--help|--document are dispatched by parse_args and never get here.

            # Now that we have parsed the args, reload the settings so the flags will
            # take effect.  These will use the values from the properties.
            old_user_output_enabled = log.SetUserOutputEnabled(None)
            old_verbosity = log.SetVerbosity(None)

            # Set the command_name property so it is persisted until the process ends.
            # Only do this for the top level command that can be detected by looking
            # at the stack. It will have one initial level, and another level added by
            # the PushInvocationValues earlier in this method.
            if len(properties.VALUES.GetInvocationStack()) == 2:
                properties.VALUES.metrics.command_name.Set(command_path_string)
            # Set the invocation value for all commands, this is lost when popped
            properties.VALUES.SetInvocationValue(
                properties.VALUES.metrics.command_name, command_path_string,
                None)

            if properties.VALUES.core.capture_session_file.Get() is not None:
                capturer = session_capturer.SessionCapturer()
                capturer.CaptureArgs(args)
                # Create a new unique random seed: the state is different each run and
                # hashes will be different with high probability
                random_seed = hash(random.getstate())
                random.seed(random_seed)
                capturer.CaptureState(
                    interactive_console=console_io.IsInteractive(),
                    random_seed=random_seed,
                    term_size=console_attr_os.GetTermSize())
                capturer.CaptureProperties(properties.VALUES.AllValues())
                session_capturer.SessionCapturer.capturer = capturer

            for hook in self.__pre_run_hooks:
                hook.Run(command_path_string)

            resources = args.calliope_command.Run(cli=self, args=args)

            for hook in self.__post_run_hooks:
                hook.Run(command_path_string)

            # Preserve generator or static list resources.

            if isinstance(resources, types.GeneratorType):

                def _Yield():
                    """Activates generator exceptions."""
                    try:
                        for resource in resources:
                            yield resource
                    except Exception as exc:  # pylint: disable=broad-except
                        self._HandleAllErrors(exc, command_path_string,
                                              specified_arg_names)

                return _Yield()

            # Do this last. If there is an error, the error handler will log the
            # command execution along with the error.
            metrics.Commands(command_path_string, config.CLOUD_SDK_VERSION,
                             specified_arg_names)
            return resources

        except Exception as exc:  # pylint: disable=broad-except
            self._HandleAllErrors(exc, command_path_string,
                                  specified_arg_names)

        finally:
            if session_capturer.SessionCapturer.capturer is not None:
                with open(properties.VALUES.core.capture_session_file.Get(),
                          'w') as f:
                    session_capturer.SessionCapturer.capturer.Print(f)
            properties.VALUES.PopInvocationValues()
            named_configs.FLAG_OVERRIDE_STACK.Pop()
            # Reset these values to their previous state now that we popped the flag
            # values.
            if old_user_output_enabled is not None:
                log.SetUserOutputEnabled(old_user_output_enabled)
            if old_verbosity is not None:
                log.SetVerbosity(old_verbosity)