Beispiel #1
0
  def _HandleAllErrors(self, exc, command_path_string, specified_arg_names):
    """Handle all errors.

    Args:
      exc: Exception, The exception that was raised.
      command_path_string: str, The '.' separated command path.
      specified_arg_names: [str], The specified arg named scrubbed for metrics.

    Raises:
      exc or a core.exceptions variant that does not produce a stack trace.
    """
    if session_capturer.SessionCapturer.capturer:
      session_capturer.SessionCapturer.capturer.CaptureException(exc)
    error_extra_info = {'error_code': getattr(exc, 'exit_code', 1)}
    if isinstance(exc, exceptions.HttpException):
      error_extra_info['http_status_code'] = exc.payload.status_code

    metrics.Commands(
        command_path_string, config.CLOUD_SDK_VERSION, specified_arg_names,
        error=exc.__class__, error_extra_info=error_extra_info)
    metrics.Error(command_path_string, exc.__class__, specified_arg_names,
                  error_extra_info=error_extra_info)

    exceptions.HandleError(exc, command_path_string, self.__known_error_handler)
  def _ReportErrorMetricsHelper(self, dotted_command_path, error,
                                error_extra_info=None):
    """Logs `Commands` and `Error` Google Analytics events for an error.

    Args:
      dotted_command_path: str, The dotted path to as much of the command as we
          can identify before an error. Example: gcloud.projects
      error: class, The class (not the instance) of the Exception for an error.
      error_extra_info: {str: json-serializable}, A json serializable dict of
        extra info that we want to log with the error. This enables us to write
        queries that can understand the keys and values in this dict.
    """
    specified_args = self.GetSpecifiedArgNames()
    metrics.Commands(
        dotted_command_path,
        config.CLOUD_SDK_VERSION,
        specified_args,
        error=error,
        error_extra_info=error_extra_info)
    metrics.Error(
        dotted_command_path,
        error,
        specified_args,
        error_extra_info=error_extra_info)
Beispiel #3
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)
            calliope_command = args._GetCommand()  # pylint: disable=protected-access
            command_path_string = '.'.join(calliope_command.GetPath())
            if not 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)
                capturer.CaptureState()
                capturer.CaptureProperties(properties.VALUES.AllValues())
                session_capturer.SessionCapturer.capturer = capturer

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

            resources = 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)
Beispiel #4
0
  def Execute(self, args=None, call_arg_complete=True):
    """Execute the CLI tool with the given arguments.

    Args:
      args: 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.
    """
    if call_arg_complete:
      self._ArgComplete()

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

    for s in args:
      try:
        s.decode('ascii')
      except UnicodeError:
        raise exceptions.InvalidStringException(s)

    # Set the command name in case an exception happens before the command name
    # is finished parsing.
    command_path_string = self.__name
    try:
      properties.VALUES.PushInvocationValues()
      args = self.__parser.parse_args(args)
      # -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.
      log.SetUserOutputEnabled(None)
      log.SetVerbosity(None)

      command_path_string = '.'.join(args.command_path)
      # TODO(user): put a real version here
      metrics.Commands(command_path_string, None)

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

      result = args.cmd_func(cli=self, args=args)

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

      return result

    except exceptions.ExitCodeNoError as exc:
      self._HandleKnownError(command_path_string, exc, print_error=False)
    except core_exceptions.Error as exc:
      self._HandleKnownError(command_path_string, exc, print_error=True)
    except Exception as exc:
      # Make sure any uncaught exceptions still make it into the log file.
      log.file_only_logger.debug(str(exc), exc_info=sys.exc_info())
      metrics.Error(command_path_string, exc)
      raise
    finally:
      properties.VALUES.PopInvocationValues()
      # Reset these values to their previous state now that we popped the flag
      # values.
      log.SetUserOutputEnabled(None)
      log.SetVerbosity(None)
Beispiel #5
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 type(args) is str:
            raise ValueError(
                'Execute expects an iterable of strings, not a string.')

        if call_arg_complete:
            self._ArgComplete()

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

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

        # 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()

        flag_names = None
        try:
            for s in args:
                try:
                    s.decode('ascii')
                except UnicodeDecodeError:
                    raise exceptions.InvalidCharacterInArgException(
                        [sys.argv[0]] + args, s)

            args = self.__parser.parse_args(args)
            flag_names = self.__parser.GetFlagCollection()
            # -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.
            log.SetUserOutputEnabled(None)
            log.SetVerbosity(None)

            command_path_string = '.'.join(args.command_path)
            properties.VALUES.SetInvocationValue(
                properties.VALUES.metrics.command_name, command_path_string,
                None)
            metrics.Commands(command_path_string, config.CLOUD_SDK_VERSION,
                             flag_names)

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

            result = args.cmd_func(cli=self, args=args)

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

            return result

        except exceptions.ExitCodeNoError as exc:
            self._HandleKnownError(command_path_string,
                                   exc,
                                   flag_names,
                                   print_error=False)
        except core_exceptions.Error as exc:
            self._HandleKnownError(command_path_string,
                                   exc,
                                   flag_names,
                                   print_error=True)
        except Exception as exc:
            if type(exc) in KNOWN_ERRORS:
                self._HandleKnownError(command_path_string,
                                       exc,
                                       flag_names,
                                       print_error=True)
            else:
                # Make sure any uncaught exceptions still make it into the log file.
                exc_printable = self.SafeExceptionToString(exc)
                log.debug(exc_printable, exc_info=sys.exc_info())
                metrics.Error(command_path_string, exc, flag_names)
                raise
        finally:
            properties.VALUES.PopInvocationValues()
            named_configs.FLAG_OVERRIDE_STACK.Pop()
            # Reset these values to their previous state now that we popped the flag
            # values.
            log.SetUserOutputEnabled(None)
            log.SetVerbosity(None)
Beispiel #6
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, six.string_types):
            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 = six.text_type
        # We need the argparse 1.2.1 patch in _SubParsersActionCall.
        # TODO(b/77288697) delete after py3 tests use non-hermetic python
        if argparse.__version__ == '1.1':
            argparse._SubParsersAction.__call__ = _SubParsersActionCall  # pylint: disable=protected-access

        if call_arg_complete:
            _ArgComplete(self.__top_element.ai)

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

        # 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

        # Convert py2 args to text.
        argv = [console_attr.Decode(arg) for arg in args] if six.PY2 else args
        old_user_output_enabled = None
        old_verbosity = None
        try:
            args = self.__parser.parse_args(_ApplyFlagsFile(argv))
            if args.CONCEPT_ARGS is not None:
                args.CONCEPT_ARGS.ParseConcepts()
            calliope_command = args._GetCommand()  # pylint: disable=protected-access
            command_path_string = '.'.join(calliope_command.GetPath())
            specified_arg_names = args.GetSpecifiedArgNames()
            # If the CLI has not been reloaded since the last command execution (e.g.
            # in test runs), args.CONCEPTS may contain cached values.
            if args.CONCEPTS is not None:
                args.CONCEPTS.Reset()

            # -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)

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

            resources = 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:
            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)
Beispiel #7
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:
            self._ArgComplete()

        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
        flag_collection = None

        argv = self._ConvertNonAsciiArgsToUnicode(args)
        try:
            args = self.__parser.parse_args(argv)
            command_path_string = '.'.join(args.command_path)
            if not args.calliope_command.IsUnicodeSupported():
                self._EnforceAsciiArgs(argv)
            flag_collection = self.__parser.GetFlagCollection()

            # -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.
            log.SetUserOutputEnabled(None)
            log.SetVerbosity(None)

            properties.VALUES.SetInvocationValue(
                properties.VALUES.metrics.command_name, command_path_string,
                None)
            metrics.Commands(command_path_string, config.CLOUD_SDK_VERSION,
                             flag_collection)

            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,
                                              flag_collection)

                return _Yield()

            return resources

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

        finally:
            properties.VALUES.PopInvocationValues()
            named_configs.FLAG_OVERRIDE_STACK.Pop()
            # Reset these values to their previous state now that we popped the flag
            # values.
            log.SetUserOutputEnabled(None)
            log.SetVerbosity(None)
Beispiel #8
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 type(args) is str:
            raise ValueError(
                'Execute expects an iterable of strings, not a string.')

        if call_arg_complete:
            self._ArgComplete()

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

        for s in args:
            try:
                s.decode('ascii')
            except (UnicodeEncodeError, UnicodeError):
                try:
                    s_printable = s.decode('utf-8')
                except (UnicodeEncodeError, UnicodeError):
                    s_printable = repr(s)

                raise exceptions.InvalidStringException(s_printable)

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

        named_configs.FLAG_OVERRIDE_STACK.AllocateFrame()
        properties.VALUES.PushInvocationValues()
        try:
            # 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.ReplaceTop(
                named_configs.AdhocConfigFlagParse(args),
                properties.PropertiesFile.Invalidate)
            args = self.__parser.parse_args(args)

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

            # In principle it's possible that the initial round of ad-hoc parsing of
            # --configuration would not agree with the later parse by argparse.  The
            # following warning is intended to be dead code, but just in case...
            if named_configs.FLAG_OVERRIDE_STACK.Peek() != args.configuration:
                log.warn('Problem parsing --configration flag.  Using named '
                         'flag value --configuration=[{0}].'.format(
                             named_configs.FLAG_OVERRIDE_STACK.Peek()))

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

            command_path_string = '.'.join(args.command_path)
            metrics.Commands(command_path_string, config.CLOUD_SDK_VERSION)

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

            result = args.cmd_func(cli=self, args=args)

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

            return result

        except exceptions.ExitCodeNoError as exc:
            self._HandleKnownError(command_path_string, exc, print_error=False)
        except core_exceptions.Error as exc:
            self._HandleKnownError(command_path_string, exc, print_error=True)
        except Exception as exc:
            if type(exc) in KNOWN_ERRORS:
                self._HandleKnownError(command_path_string,
                                       exc,
                                       print_error=True)
            else:
                # Make sure any uncaught exceptions still make it into the log file.
                exc_printable = self.SafeExceptionToString(exc)
                log.file_only_logger.debug(exc_printable,
                                           exc_info=sys.exc_info())
                metrics.Error(command_path_string, exc)
                raise
        finally:
            properties.VALUES.PopInvocationValues()
            named_configs.FLAG_OVERRIDE_STACK.Pop()
            # Reset these values to their previous state now that we popped the flag
            # values.
            log.SetUserOutputEnabled(None)
            log.SetVerbosity(None)