Exemple #1
0
  def RunCommand(self):
    (help_type_map, help_name_map) = self._LoadHelpMaps()
    output = []
    if not len(self.args):
      output.append('%s\nAvailable commands:\n' % top_level_usage_string)
      format_str = '  %-' + str(MAX_HELP_NAME_LEN) + 's%s\n'
      for help_prov in sorted(help_type_map[HelpType.COMMAND_HELP],
                              key=lambda hp: hp.help_spec[HELP_NAME]):
        output.append(format_str % (help_prov.help_spec[HELP_NAME],
                                    help_prov.help_spec[HELP_ONE_LINE_SUMMARY]))
      output.append('\nAdditional help topics:\n')
      for help_prov in sorted(help_type_map[HelpType.ADDITIONAL_HELP],
                              key=lambda hp: hp.help_spec[HELP_NAME]):
        output.append(format_str % (help_prov.help_spec[HELP_NAME],
                                    help_prov.help_spec[HELP_ONE_LINE_SUMMARY]))
      output.append('\nUse gsutil help <command or topic> for detailed help.')
    else:
      arg = self.args[0]
      if arg not in help_name_map:
        output.append('No help available for "%s"' % arg)
      else:
        help_prov = help_name_map[self.args[0]]
        output.append('<B>NAME</B>\n')
        output.append('  %s - %s\n' % (
          help_prov.help_spec[HELP_NAME],
          help_prov.help_spec[HELP_ONE_LINE_SUMMARY]))
        output.append('\n\n')
        output.append(help_prov.help_spec[HELP_TEXT].strip('\n'))
        new_alias = OLD_ALIAS_MAP.get(arg, [None])[0]
        if new_alias:
          deprecation_warning = """
  The "%s" alias is deprecated, and will eventually be removed completely.
  Please use the "%s" command instead.""" % (arg, new_alias)

          output.append('\n\n\n<B>DEPRECATION WARNING</B>\n')
          output.append(deprecation_warning)
    self._OutputHelp(''.join(output))
    return 0
Exemple #2
0
  def RunCommand(self):
    """Command entry point for the help command."""
    (help_type_map, help_name_map) = self._LoadHelpMaps()
    output = []
    if not self.args:
      output.append('%s\nAvailable commands:\n' % top_level_usage_string)
      format_str = '  %-' + str(MAX_HELP_NAME_LEN) + 's%s\n'
      for help_prov in sorted(help_type_map['command_help'],
                              key=lambda hp: hp.help_spec.help_name):
        output.append(format_str % (help_prov.help_spec.help_name,
                                    help_prov.help_spec.help_one_line_summary))
      output.append('\nAdditional help topics:\n')
      for help_prov in sorted(help_type_map['additional_help'],
                              key=lambda hp: hp.help_spec.help_name):
        output.append(format_str % (help_prov.help_spec.help_name,
                                    help_prov.help_spec.help_one_line_summary))
      output.append('\nUse gsutil help <command or topic> for detailed help.')
    else:
      invalid_subcommand = False
      arg = self.args[0]
      if arg not in help_name_map:
        output.append('No help available for "%s"' % arg)
      else:
        help_prov = help_name_map[arg]
        help_name = None
        if len(self.args) > 1:  # We also have a subcommand argument.
          subcommand_map = help_prov.help_spec.subcommand_help_text
          if subcommand_map and self.args[1] in subcommand_map:
            help_name = arg + ' ' + self.args[1]
            help_text = subcommand_map[self.args[1]]
          else:
            invalid_subcommand = True
            if not subcommand_map:
              output.append(
                  ('The "%s" command has no subcommands. You can ask for the '
                   'full help by running:\n\n\tgsutil help %s\n') % (arg, arg))
            else:
              subcommand_examples = []
              for subcommand in subcommand_map:
                subcommand_examples.append('\tgsutil help %s %s' %
                                           (arg, subcommand))
              output.append(
                  ('Subcommand "%s" does not exist for command "%s".\n'
                   'You can either ask for the full help about the command by '
                   'running:\n\n\tgsutil help %s\n\n'
                   'Or you can ask for help about one of the subcommands:\n\n%s'
                  ) % (self.args[1], arg, arg, '\n'.join(subcommand_examples)))
        if not invalid_subcommand:
          if not help_name:  # No subcommand or invalid subcommand.
            help_name = help_prov.help_spec.help_name
            help_text = help_prov.help_spec.help_text

          output.append('<B>NAME</B>\n')
          output.append('  %s - %s\n' %
                        (help_name, help_prov.help_spec.help_one_line_summary))
          output.append('\n\n')
          output.append(help_text.strip('\n'))
          new_alias = OLD_ALIAS_MAP.get(arg, [None])[0]
          if new_alias:
            deprecation_warning = """
  The "%s" alias is deprecated, and will eventually be removed completely.
  Please use the "%s" command instead.""" % (arg, new_alias)

            output.append('\n\n\n<B>DEPRECATION WARNING</B>\n')
            output.append(deprecation_warning)
    self._OutputHelp(''.join(output))
    return 0
Exemple #3
0
  def RunNamedCommand(self, command_name, args=None, headers=None, debug=0,
                      parallel_operations=False, test_method=None,
                      skip_update_check=False, logging_filters=None,
                      do_shutdown=True):
    """Runs the named command.

    Used by gsutil main, commands built atop other commands, and tests.

    Args:
      command_name: The name of the command being run.
      args: Command-line args (arg0 = actual arg, not command name ala bash).
      headers: Dictionary containing optional HTTP headers to pass to boto.
      debug: Debug level to pass in to boto connection (range 0..3).
      parallel_operations: Should command operations be executed in parallel?
      test_method: Optional general purpose method for testing purposes.
                   Application and semantics of this method will vary by
                   command and test type.
      skip_update_check: Set to True to disable checking for gsutil updates.
      logging_filters: Optional list of logging.Filters to apply to this
                       command's logger.
      do_shutdown: Stop all parallelism framework workers iff this is True.

    Raises:
      CommandException: if errors encountered.

    Returns:
      Return value(s) from Command that was run.
    """
    if (not skip_update_check and
        self.MaybeCheckForAndOfferSoftwareUpdate(command_name, debug)):
      command_name = 'update'
      args = ['-n']

    if not args:
      args = []

    # Include api_version header in all commands.
    api_version = boto.config.get_value('GSUtil', 'default_api_version', '1')
    if not headers:
      headers = {}
    headers['x-goog-api-version'] = api_version

    if command_name not in self.command_map:
      close_matches = difflib.get_close_matches(
          command_name, self.command_map.keys(), n=1)
      if close_matches:
        # Instead of suggesting a deprecated command alias, suggest the new
        # name for that command.
        translated_command_name = (
            OLD_ALIAS_MAP.get(close_matches[0], close_matches)[0])
        print >> sys.stderr, 'Did you mean this?'
        print >> sys.stderr, '\t%s' % translated_command_name
      raise CommandException('Invalid command "%s".' % command_name)
    if '--help' in args:
      new_args = [command_name]
      original_command_class = self.command_map[command_name]
      subcommands = original_command_class.help_spec.subcommand_help_text.keys()
      for arg in args:
        if arg in subcommands:
          new_args.append(arg)
          break  # Take the first match and throw away the rest.
      args = new_args
      command_name = 'help'

    args = HandleArgCoding(args)

    command_class = self.command_map[command_name]
    command_inst = command_class(
        self, args, headers, debug, parallel_operations,
        self.bucket_storage_uri_class, self.gsutil_api_class_map_factory,
        test_method, logging_filters, command_alias_used=command_name)
    return_code = command_inst.RunCommand()

    if MultiprocessingIsAvailable()[0] and do_shutdown:
      ShutDownGsutil()
    if GetFailureCount() > 0:
      return_code = 1
    return return_code
  def RunNamedCommand(self, command_name, args=None, headers=None, debug=0,
                      trace_token=None, parallel_operations=False,
                      skip_update_check=False, logging_filters=None,
                      do_shutdown=True, perf_trace_token=None,
                      user_project=None,
                      collect_analytics=False):
    """Runs the named command.

    Used by gsutil main, commands built atop other commands, and tests.

    Args:
      command_name: The name of the command being run.
      args: Command-line args (arg0 = actual arg, not command name ala bash).
      headers: Dictionary containing optional HTTP headers to pass to boto.
      debug: Debug level to pass in to boto connection (range 0..3).
      trace_token: Trace token to pass to the underlying API.
      parallel_operations: Should command operations be executed in parallel?
      skip_update_check: Set to True to disable checking for gsutil updates.
      logging_filters: Optional list of logging.Filters to apply to this
          command's logger.
      do_shutdown: Stop all parallelism framework workers iff this is True.
      perf_trace_token: Performance measurement trace token to pass to the
          underlying API.
      user_project: The project to bill this request to.
      collect_analytics: Set to True to collect an analytics metric logging this
          command.

    Raises:
      CommandException: if errors encountered.

    Returns:
      Return value(s) from Command that was run.
    """
    command_changed_to_update = False
    if (not skip_update_check and
        self.MaybeCheckForAndOfferSoftwareUpdate(command_name, debug)):
      command_name = 'update'
      command_changed_to_update = True
      args = ['-n']

      # Check for opt-in analytics.
      if IsRunningInteractively() and collect_analytics:
        metrics.CheckAndMaybePromptForAnalyticsEnabling()

    if not args:
      args = []

    # Include api_version header in all commands.
    api_version = boto.config.get_value('GSUtil', 'default_api_version', '1')
    if not headers:
      headers = {}
    headers['x-goog-api-version'] = api_version

    if command_name not in self.command_map:
      close_matches = difflib.get_close_matches(
          command_name, self.command_map.keys(), n=1)
      if close_matches:
        # Instead of suggesting a deprecated command alias, suggest the new
        # name for that command.
        translated_command_name = (
            OLD_ALIAS_MAP.get(close_matches[0], close_matches)[0])
        print >> sys.stderr, 'Did you mean this?'
        print >> sys.stderr, '\t%s' % translated_command_name
      elif command_name == 'update' and gslib.IS_PACKAGE_INSTALL:
        sys.stderr.write(
            'Update command is not supported for package installs; '
            'please instead update using your package manager.')

      raise CommandException('Invalid command "%s".' % command_name)
    if '--help' in args:
      new_args = [command_name]
      original_command_class = self.command_map[command_name]
      subcommands = original_command_class.help_spec.subcommand_help_text.keys()
      for arg in args:
        if arg in subcommands:
          new_args.append(arg)
          break  # Take the first match and throw away the rest.
      args = new_args
      command_name = 'help'

    HandleArgCoding(args)
    HandleHeaderCoding(headers)

    command_class = self.command_map[command_name]
    command_inst = command_class(
        self, args, headers, debug, trace_token, parallel_operations,
        self.bucket_storage_uri_class, self.gsutil_api_class_map_factory,
        logging_filters, command_alias_used=command_name,
        perf_trace_token=perf_trace_token, user_project=user_project)

    # Log the command name, command alias, and sub-options after being parsed by
    # RunCommand and the command constructor. For commands with subcommands and
    # suboptions, we need to log the suboptions again within the command itself
    # because the command constructor will not parse the suboptions fully.
    if collect_analytics:
      metrics.LogCommandParams(command_name=command_inst.command_name,
                               sub_opts=command_inst.sub_opts,
                               command_alias=command_name)

    return_code = command_inst.RunCommand()

    if CheckMultiprocessingAvailableAndInit().is_available and do_shutdown:
      ShutDownGsutil()
    if GetFailureCount() > 0:
      return_code = 1
    if command_changed_to_update:
      # If the command changed to update, the user's original command was
      # not executed.
      return_code = 1
      print '\n'.join(textwrap.wrap(
          'Update was successful. Exiting with code 1 as the original command '
          'issued prior to the update was not executed and should be re-run.'))
    return return_code
Exemple #5
0
    def RunNamedCommand(self,
                        command_name,
                        args=None,
                        headers=None,
                        debug=0,
                        trace_token=None,
                        parallel_operations=False,
                        skip_update_check=False,
                        logging_filters=None,
                        do_shutdown=True,
                        perf_trace_token=None,
                        user_project=None,
                        collect_analytics=False):
        """Runs the named command.

    Used by gsutil main, commands built atop other commands, and tests.

    Args:
      command_name: The name of the command being run.
      args: Command-line args (arg0 = actual arg, not command name ala bash).
      headers: Dictionary containing optional HTTP headers to pass to boto.
      debug: Debug level to pass in to boto connection (range 0..3).
      trace_token: Trace token to pass to the underlying API.
      parallel_operations: Should command operations be executed in parallel?
      skip_update_check: Set to True to disable checking for gsutil updates.
      logging_filters: Optional list of logging.Filters to apply to this
          command's logger.
      do_shutdown: Stop all parallelism framework workers iff this is True.
      perf_trace_token: Performance measurement trace token to pass to the
          underlying API.
      user_project: The project to bill this request to.
      collect_analytics: Set to True to collect an analytics metric logging this
          command.

    Raises:
      CommandException: if errors encountered.

    Returns:
      Return value(s) from Command that was run.
    """
        command_changed_to_update = False
        if (not skip_update_check and self.MaybeCheckForAndOfferSoftwareUpdate(
                command_name, debug)):
            command_name = 'update'
            command_changed_to_update = True
            args = ['-n']

            # Check for opt-in analytics.
            if system_util.IsRunningInteractively() and collect_analytics:
                metrics.CheckAndMaybePromptForAnalyticsEnabling()

        if not args:
            args = []

        # Include api_version header in all commands.
        api_version = boto.config.get_value('GSUtil', 'default_api_version',
                                            '1')
        if not headers:
            headers = {}
        headers['x-goog-api-version'] = api_version

        if command_name not in self.command_map:
            close_matches = difflib.get_close_matches(command_name,
                                                      self.command_map.keys(),
                                                      n=1)
            if close_matches:
                # Instead of suggesting a deprecated command alias, suggest the new
                # name for that command.
                translated_command_name = (OLD_ALIAS_MAP.get(
                    close_matches[0], close_matches)[0])
                print >> sys.stderr, 'Did you mean this?'
                print >> sys.stderr, '\t%s' % translated_command_name
            elif command_name == 'update' and gslib.IS_PACKAGE_INSTALL:
                sys.stderr.write(
                    'Update command is not supported for package installs; '
                    'please instead update using your package manager.')

            raise CommandException('Invalid command "%s".' % command_name)
        if '--help' in args:
            new_args = [command_name]
            original_command_class = self.command_map[command_name]
            subcommands = original_command_class.help_spec.subcommand_help_text.keys(
            )
            for arg in args:
                if arg in subcommands:
                    new_args.append(arg)
                    break  # Take the first match and throw away the rest.
            args = new_args
            command_name = 'help'

        HandleArgCoding(args)
        HandleHeaderCoding(headers)

        command_class = self.command_map[command_name]
        command_inst = command_class(self,
                                     args,
                                     headers,
                                     debug,
                                     trace_token,
                                     parallel_operations,
                                     self.bucket_storage_uri_class,
                                     self.gsutil_api_class_map_factory,
                                     logging_filters,
                                     command_alias_used=command_name,
                                     perf_trace_token=perf_trace_token,
                                     user_project=user_project)

        # Log the command name, command alias, and sub-options after being parsed by
        # RunCommand and the command constructor. For commands with subcommands and
        # suboptions, we need to log the suboptions again within the command itself
        # because the command constructor will not parse the suboptions fully.
        if collect_analytics:
            metrics.LogCommandParams(command_name=command_inst.command_name,
                                     sub_opts=command_inst.sub_opts,
                                     command_alias=command_name)

        return_code = command_inst.RunCommand()

        if CheckMultiprocessingAvailableAndInit().is_available and do_shutdown:
            ShutDownGsutil()
        if GetFailureCount() > 0:
            return_code = 1
        if command_changed_to_update:
            # If the command changed to update, the user's original command was
            # not executed.
            return_code = 1
            print '\n'.join(
                textwrap.wrap(
                    'Update was successful. Exiting with code 1 as the original command '
                    'issued prior to the update was not executed and should be re-run.'
                ))
        return return_code
Exemple #6
0
    def RunCommand(self):
        """Command entry point for the help command."""
        (help_type_map, help_name_map) = self._LoadHelpMaps()
        output = []
        if not self.args:
            output.append("%s\nAvailable commands:\n" % top_level_usage_string)
            format_str = "  %-" + str(MAX_HELP_NAME_LEN) + "s%s\n"
            for help_prov in sorted(help_type_map["command_help"], key=lambda hp: hp.help_spec.help_name):
                output.append(format_str % (help_prov.help_spec.help_name, help_prov.help_spec.help_one_line_summary))
            output.append("\nAdditional help topics:\n")
            for help_prov in sorted(help_type_map["additional_help"], key=lambda hp: hp.help_spec.help_name):
                output.append(format_str % (help_prov.help_spec.help_name, help_prov.help_spec.help_one_line_summary))
            output.append("\nUse gsutil help <command or topic> for detailed help.")
        else:
            invalid_subcommand = False
            arg = self.args[0]
            if arg not in help_name_map:
                output.append('No help available for "%s"' % arg)
            else:
                help_prov = help_name_map[arg]
                help_name = None
                if len(self.args) > 1:  # We also have a subcommand argument.
                    subcommand_map = help_prov.help_spec.subcommand_help_text
                    if subcommand_map and self.args[1] in subcommand_map:
                        help_name = arg + " " + self.args[1]
                        help_text = subcommand_map[self.args[1]]
                    else:
                        invalid_subcommand = True
                        if not subcommand_map:
                            output.append(
                                (
                                    'The "%s" command has no subcommands. You can ask for the '
                                    "full help by running:\n\n\tgsutil help %s\n"
                                )
                                % (arg, arg)
                            )
                        else:
                            subcommand_examples = []
                            for subcommand in subcommand_map:
                                subcommand_examples.append("\tgsutil help %s %s" % (arg, subcommand))
                            output.append(
                                (
                                    'Subcommand "%s" does not exist for command "%s".\n'
                                    "You can either ask for the full help about the command by "
                                    "running:\n\n\tgsutil help %s\n\n"
                                    "Or you can ask for help about one of the subcommands:\n\n%s"
                                )
                                % (self.args[1], arg, arg, "\n".join(subcommand_examples))
                            )
                if not invalid_subcommand:
                    if not help_name:  # No subcommand or invalid subcommand.
                        help_name = help_prov.help_spec.help_name
                        help_text = help_prov.help_spec.help_text

                    output.append("<B>NAME</B>\n")
                    output.append("  %s - %s\n" % (help_name, help_prov.help_spec.help_one_line_summary))
                    output.append("\n\n")
                    output.append(help_text.strip("\n"))
                    new_alias = OLD_ALIAS_MAP.get(arg, [None])[0]
                    if new_alias:
                        deprecation_warning = """
  The "%s" alias is deprecated, and will eventually be removed completely.
  Please use the "%s" command instead.""" % (
                            arg,
                            new_alias,
                        )

                        output.append("\n\n\n<B>DEPRECATION WARNING</B>\n")
                        output.append(deprecation_warning)
        self._OutputHelp("".join(output))
        return 0
Exemple #7
0
  def RunNamedCommand(self, command_name, args=None, headers=None, debug=0,
                      parallel_operations=False, test_method=None,
                      skip_update_check=False, logging_filters=None,
                      do_shutdown=True):
    """Runs the named command.

    Used by gsutil main, commands built atop other commands, and tests.

    Args:
      command_name: The name of the command being run.
      args: Command-line args (arg0 = actual arg, not command name ala bash).
      headers: Dictionary containing optional HTTP headers to pass to boto.
      debug: Debug level to pass in to boto connection (range 0..3).
      parallel_operations: Should command operations be executed in parallel?
      test_method: Optional general purpose method for testing purposes.
                   Application and semantics of this method will vary by
                   command and test type.
      skip_update_check: Set to True to disable checking for gsutil updates.
      logging_filters: Optional list of logging.Filters to apply to this
                       command's logger.
      do_shutdown: Stop all parallelism framework workers iff this is True.

    Raises:
      CommandException: if errors encountered.

    Returns:
      Return value(s) from Command that was run.
    """
    if (not skip_update_check and
        self.MaybeCheckForAndOfferSoftwareUpdate(command_name, debug)):
      command_name = 'update'
      args = ['-n']

    if not args:
      args = []

    # Include api_version header in all commands.
    api_version = boto.config.get_value('GSUtil', 'default_api_version', '1')
    if not headers:
      headers = {}
    headers['x-goog-api-version'] = api_version

    if command_name not in self.command_map:
      close_matches = difflib.get_close_matches(
          command_name, self.command_map.keys(), n=1)
      if close_matches:
        # Instead of suggesting a deprecated command alias, suggest the new
        # name for that command.
        translated_command_name = (
            OLD_ALIAS_MAP.get(close_matches[0], close_matches)[0])
        print >> sys.stderr, 'Did you mean this?'
        print >> sys.stderr, '\t%s' % translated_command_name
      raise CommandException('Invalid command "%s".' % command_name)
    if '--help' in args:
      new_args = [command_name]
      original_command_class = self.command_map[command_name]
      subcommands = original_command_class.help_spec.subcommand_help_text.keys()
      for arg in args:
        if arg in subcommands:
          new_args.append(arg)
          break  # Take the first match and throw away the rest.
      args = new_args
      command_name = 'help'

    args = HandleArgCoding(args)

    command_class = self.command_map[command_name]
    command_inst = command_class(
        self, args, headers, debug, parallel_operations,
        self.bucket_storage_uri_class, self.gsutil_api_class_map_factory,
        test_method, logging_filters, command_alias_used=command_name)
    return_code = command_inst.RunCommand()

    if MultiprocessingIsAvailable()[0] and do_shutdown:
      ShutDownGsutil()
    if GetFailureCount() > 0:
      return_code = 1
    return return_code
Exemple #8
0
  def RunNamedCommand(self, command_name, args=None, headers=None, debug=0,
                      parallel_operations=False, test_method=None,
                      skip_update_check=False, logging_filters=None):
    """Runs the named command. Used by gsutil main, commands built atop
      other commands, and tests .

      Args:
        command_name: The name of the command being run.
        args: Command-line args (arg0 = actual arg, not command name ala bash).
        headers: Dictionary containing optional HTTP headers to pass to boto.
        debug: Debug level to pass in to boto connection (range 0..3).
        parallel_operations: Should command operations be executed in parallel?
        test_method: Optional general purpose method for testing purposes.
                     Application and semantics of this method will vary by
                     command and test type.
        skip_update_check: Set to True to disable checking for gsutil updates.
        logging_filters: Optional list of logging.Filters to apply to this
                         command's logger.

      Raises:
        CommandException: if errors encountered.
    """
    ConfigureNoOpAuthIfNeeded()
    if (not skip_update_check and
        self._MaybeCheckForAndOfferSoftwareUpdate(command_name, debug)):
      command_name = 'update'
      args = ['-n']

    if not args:
      args = []

    # Include api_version header in all commands.
    api_version = boto.config.get_value('GSUtil', 'default_api_version', '1')
    if not headers:
      headers = {}
    headers['x-goog-api-version'] = api_version

    if command_name not in self.command_map:
      close_matches = difflib.get_close_matches(
          command_name, self.command_map.keys(), n=1)
      if len(close_matches):
        # Instead of suggesting a deprecated command alias, suggest the new
        # name for that command.
        translated_command_name = (
            OLD_ALIAS_MAP.get(close_matches[0], close_matches)[0])
        print >> sys.stderr, 'Did you mean this?'
        print >> sys.stderr, '\t%s' % translated_command_name
      raise CommandException('Invalid command "%s".' % command_name)
    if '--help' in args:
      args = [command_name]
      command_name = 'help'

    # Python passes arguments from the command line as byte strings. To
    # correctly interpret them, we decode them as utf-8 here.
    args = [a.decode('utf-8') for a in args]

    command_class = self.command_map[command_name]
    command_inst = command_class(
        self, args, headers, debug, parallel_operations, self.config_file_list,
        self.bucket_storage_uri_class, test_method, logging_filters,
        command_alias_used=command_name)
    return command_inst.RunCommand()
Exemple #9
0
  def RunCommand(self):
    (help_type_map, help_name_map) = self._LoadHelpMaps()
    output = []
    if not len(self.args):
      output.append('%s\nAvailable commands:\n' % top_level_usage_string)
      format_str = '  %-' + str(MAX_HELP_NAME_LEN) + 's%s\n'
      for help_prov in sorted(help_type_map[HelpType.COMMAND_HELP],
                              key=lambda hp: hp.help_spec[HELP_NAME]):
        output.append(format_str % (help_prov.help_spec[HELP_NAME],
                                    help_prov.help_spec[HELP_ONE_LINE_SUMMARY]))
      output.append('\nAdditional help topics:\n')
      for help_prov in sorted(help_type_map[HelpType.ADDITIONAL_HELP],
                              key=lambda hp: hp.help_spec[HELP_NAME]):
        output.append(format_str % (help_prov.help_spec[HELP_NAME],
                                    help_prov.help_spec[HELP_ONE_LINE_SUMMARY]))
      output.append('\nUse gsutil help <command or topic> for detailed help.')
    else:
      invalid_subcommand = False
      arg = self.args[0]
      if arg not in help_name_map:
        output.append('No help available for "%s"' % arg)
      else:
        help_prov = help_name_map[arg]
        help_name = None
        if len(self.args) > 1:  # We also have a subcommand argument.
          subcommand_map = help_prov.help_spec.get(SUBCOMMAND_HELP_TEXT, None)
          if subcommand_map and self.args[1] in subcommand_map:
            help_name = arg + ' ' + self.args[1]
            help_text = subcommand_map[self.args[1]]
          else:
            invalid_subcommand = True
            if not subcommand_map:
              output.append((
                  'The "%s" command has no subcommands. You can ask for the ' +
                  'full help by running:\n\n\tgsutil help %s\n') %
                  (arg, arg))
            else:
              subcommand_examples = []
              for subcommand in subcommand_map:
                subcommand_examples.append(
                    '\tgsutil help %s %s' % (arg, subcommand))
              output.append(
                  ('Subcommand "%s" does not exist for command "%s".\n' +
                  'You can either ask for the full help about the command by ' +
                  'running:\n\n\tgsutil help %s\n\n'
                  'Or you can ask for help about one of the subcommands:\n\n%s'
                  ) % (self.args[1], arg, arg, '\n'.join(subcommand_examples)))
        if not invalid_subcommand:
          if not help_name:  # No subcommand or invalid subcommand.
            help_name = help_prov.help_spec[HELP_NAME]
            help_text = help_prov.help_spec[HELP_TEXT]

          output.append('<B>NAME</B>\n')
          output.append('  %s - %s\n' %
                        (help_name, help_prov.help_spec[HELP_ONE_LINE_SUMMARY]))
          output.append('\n\n')
          output.append(help_text.strip('\n'))
          new_alias = OLD_ALIAS_MAP.get(arg, [None])[0]
          if new_alias:
            deprecation_warning = """
  The "%s" alias is deprecated, and will eventually be removed completely.
  Please use the "%s" command instead.""" % (arg, new_alias)

            output.append('\n\n\n<B>DEPRECATION WARNING</B>\n')
            output.append(deprecation_warning)
    self._OutputHelp(''.join(output))
    return 0