Beispiel #1
0
  def _WaitForOutput(self, match_strings):
    """Reads stdout from process until each string in match_strings is hit."""
    idx = 0
    output = []
    def IndexInBounds():
      return idx < len(match_strings)
    while IndexInBounds():
      line = console_attr.Decode(self.p.stdout.readline())
      if not line:
        break
      output.append(line)
      # We allow that a given line may satisfy multiple required strings
      while IndexInBounds() and match_strings[idx] in line:
        idx += 1

    if idx == len(match_strings):
      # If we arrive here, this means that every line was matched
      return

    # Did not find text
    (stdout, stderr) = self.p.communicate()
    self.result = ExecutionResult(
        command=' '.join(self.args), return_code=self.p.returncode,
        stdout=''.join(output) + console_attr.Decode(stdout),
        stderr=console_attr.Decode(stderr))
    raise ExecutionError(
        self.result, msg='Could not find required string in output')
Beispiel #2
0
 def Run(self, test_file_path, path, style, suffix, exception=None):
     file_base = '_'.join(path)
     markdown_path = self.GetTestdataPath(test_file_path,
                                          file_base + '.src.md')
     try:
         markdown_data = console_attr.Decode(
             pkg_resources.GetResourceFromFile(markdown_path))
     except IOError:
         file_base = '_'.join(['gcloud'] + path)
         markdown_path = self.GetTestdataPath(test_file_path, 'markdown',
                                              file_base + '.md')
         markdown_data = console_attr.Decode(
             pkg_resources.GetResourceFromFile(markdown_path))
     f = io.StringIO(markdown_data)
     try:
         e = None
         render_document.RenderDocument(style,
                                        fin=f,
                                        out=log.out,
                                        notes='A special note.',
                                        title=' '.join(['gcloud'] + path))
     except Exception as e:  # pylint: disable=broad-except
         e = e.message
     if e != exception:
         if not exception:
             self.fail('Exception not expected but [%s] caught.' % e)
         else:
             self.fail('Exception [%s] expected but caught [%s].' %
                       (exception, e))
     self.AssertOutputIsGolden(test_file_path, file_base + suffix)
     self.ClearOutput()
 def testDecodeException(self):
   self.assertEqual('ascii', console_attr.Decode(Exception('ascii')))
   self.assertEqual('\xff', console_attr.Decode(Exception('\xff')))
   self.assertEqual('\u1000', console_attr.Decode(Exception('\u1000')))
   if six.PY2:
     self.assertEqual('\xff', console_attr.Decode(Exception(b'\xc3\xbf')))
   else:
     # On Py3, bytes are not treated as strings and the str() of Exception
     # contains the repr(). This should be fine because on Py3 Exceptions
     # should not contain bytes anyway, and if they do, there is no expectation
     # that it can be decoded.
     self.assertEqual(
         r"b'\xc3\xbf'", console_attr.Decode(Exception(b'\xc3\xbf')))
 def testDecodeUtf8FileSyetem(self):
   self.StartObjectPatch(
       console_attr.ConsoleAttr, 'GetEncoding').return_value = 'ascii'
   self.StartObjectPatch(sys, 'getfilesystemencoding').return_value = 'utf-8'
   expected = _UNICODE
   actual = console_attr.Decode(_UTF8)
   self.assertEqual(expected, actual)
 def testDecodeIso8859_1None(self):
   self.StartObjectPatch(
       console_attr.ConsoleAttr, 'GetEncoding').return_value = 'ascii'
   self.StartObjectPatch(sys, 'getfilesystemencoding').return_value = 'ascii'
   self.StartObjectPatch(sys, 'getdefaultencoding').return_value = 'ascii'
   expected = _ISO_8859_1.decode('iso-8859-1')
   actual = console_attr.Decode(_ISO_8859_1)
   self.assertEqual(expected, actual)
Beispiel #6
0
 def _Run(self):
     try:
         self.p = subprocess.Popen(self.args,
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   env=self.env)
         (out, err) = self.p.communicate(self.stdin)
         self.result = ExecutionResult(command=' '.join(
             console_attr.SafeText(a) for a in self.args),
                                       return_code=self.p.returncode,
                                       stdout=console_attr.Decode(out),
                                       stderr=console_attr.Decode(err))
         ExecutionError.RaiseIfError(self.result)
     # pylint: disable=bare-except, not catching, just saving to raise later
     except:
         self.exc_info = sys.exc_info()
Beispiel #7
0
def _Stringify(value):  # pylint: disable=invalid-name
    """Represents value as a JSON string if it's not a string."""
    if value is None:
        return ''
    elif isinstance(value, console_attr.Colorizer):
        return value
    elif isinstance(value, six.string_types):
        return console_attr.Decode(value)
    elif isinstance(value, float):
        return resource_transform.TransformFloat(value)
    elif hasattr(value, '__str__'):
        return six.text_type(value)
    else:
        return json.dumps(value, sort_keys=True)
Beispiel #8
0
def ReadStdin(binary=False):
    """Reads data from stdin, correctly accounting for encoding.

  Anything that needs to read sys.stdin must go through this method.

  Args:
    binary: bool, True to read raw bytes, False to read text.

  Returns:
    A text string if binary is False, otherwise a byte string.
  """
    if binary:
        return files.ReadStdinBytes()
    else:
        data = sys.stdin.read()
        if six.PY2:
            # On Python 2, stdin comes in a a byte string. Convert it to text.
            data = console_attr.Decode(data)
        return data
Beispiel #9
0
  def _ConvertNonAsciiArgsToUnicode(self, args):
    """Converts non-ascii args to unicode.

    The args most likely came from sys.argv, and Python 2.7 passes them in as
    bytestrings instead of unicode.

    Args:
      args: [str], The list of args to convert.

    Raises:
      InvalidCharacterInArgException if a non-ascii arg cannot be converted to
      unicode.

    Returns:
      A new list of args with non-ascii args converted to unicode.
    """
    argv = []
    for arg in args:
      if isinstance(arg, six.text_type):
        # Arg is already a text string, just use it.
        argv.append(arg)
      else:
        # Argument is a byte string.
        try:
          # For now we want to leave byte strings as is if they are only ascii
          # characters. Later we should always convert everything to text.
          decoded_arg = arg.decode('ascii')  # If this passes, it's ascii only.
          # Add the original byte string if on PY2, for PY3 start living in the
          # text only world.
          argv.append(arg if six.PY2 else decoded_arg)
        except UnicodeError:
          # There are unicode characters in the byte string.
          try:
            argv.append(console_attr.Decode(arg))
          except UnicodeError:
            raise exceptions.InvalidCharacterInArgException(
                [self.name] + args, arg)
    return argv
Beispiel #10
0
def GetDecodedArgv():
  return [console_attr.Decode(arg) for arg in sys.argv]
Beispiel #11
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':  # pytype: disable=module-attr
      argparse._SubParsersAction.__call__ = _SubParsersActionCall  # pylint: disable=protected-access

    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

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

      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)
 def testDecodeObjectUnicode(self):
     obj = _Object(name='Ṁöë', value=".TꙅAꟻ ɘↄAlq oᴎ 'ᴎiTTɘg ɘᴙ'ɘW")
     expected = "Ṁöë=.TꙅAꟻ ɘↄAlq oᴎ 'ᴎiTTɘg ɘᴙ'ɘW"
     actual = console_attr.Decode(obj)
     self.assertEqual(expected, actual)
 def testDecodeObjectAscii(self):
     obj = _Object()
     expected = 'abc=123'
     actual = console_attr.Decode(obj)
     self.assertEqual(expected, actual)
 def testDecodeUtf8AttrKwarg(self):
     expected = _ISO_8859_1.decode('iso-8859-1')
     actual = console_attr.Decode(_ISO_8859_1, encoding='utf8')
     self.assertEqual(expected, actual)
 def testDecodeUtf8Attr(self):
     self.StartObjectPatch(console_attr.ConsoleAttr,
                           'GetEncoding').return_value = 'utf8'
     expected = _UNICODE
     actual = console_attr.Decode(_UTF8)
     self.assertEqual(expected, actual)
 def testDecodeAscii(self):
     expected = _ASCII
     actual = console_attr.Decode(_ASCII)
     self.assertEqual(expected, actual)