Esempio n. 1
0
    def SetTopLevelCommand(self, name):
        """Sets the name of the top level command for single command CLIs.

    If you are making a CLI with no subgroups, use this to set the name of the
    command to use from the command root directory.

    Args:
      name: str, The name of the command to add.  This must correspond to a
        <name>.py file in the command root directory.

    Raises:
      backend.LayoutException: If modules have already been added.
    """
        if self.__modules:
            raise backend.LayoutException(
                'You cannot set a top level command because command modules have '
                'already been added.')
        self.__top_level_command = name
Esempio n. 2
0
    def __init__(self,
                 name,
                 command_root_directory,
                 allow_non_existing_modules=False,
                 load_context=None,
                 logs_dir=config.Paths().logs_dir,
                 version_func=None):
        """Initialize Calliope.

    Args:
      name: str, The name of the top level command, used for nice error
        reporting.
      command_root_directory: str, The path to the directory containing the main
        CLI module.
      allow_non_existing_modules: True to allow extra module directories to not
        exist, False to raise an exception if a module does not exist.
      load_context: A function that returns a context dict, or None for a
        default which always returns {}.
      logs_dir: str, The path to the root directory to store logs in, or None
        for no log files.
      version_func: func, A function to call for a top-level -v and
        --version flag. If None, no flags will be available.

    Raises:
      backend.LayoutException: If no command root directory is given.
    """
        self.__name = name
        self.__command_root_directory = command_root_directory
        if not self.__command_root_directory:
            raise backend.LayoutException(
                'You must specify a command root directory.')

        self.__allow_non_existing_modules = allow_non_existing_modules

        self.__config_hooks = backend.ConfigHooks(load_context=load_context)
        self.__logs_dir = logs_dir
        self.__version_func = version_func

        self.__pre_run_hooks = []
        self.__post_run_hooks = []

        self.__modules = []
        self.__missing_components = {}
        self.__release_tracks = {}
Esempio n. 3
0
    def AddModule(self, name, path):
        """Adds a module to this CLI tool.

    If you are making a CLI that has subgroups, use this to add in more
    directories of commands.

    Args:
      name: str, The name of the group to create under the main CLI.  If this is
        to be placed under another group, a dotted name can be used.
      path: str, The full path the directory containing the commands for this
        group.

    Raises:
      backend.LayoutException: If a top level command has already been added.
    """
        if self.__top_level_command:
            raise backend.LayoutException(
                'You cannot add a module because a top level command has already '
                'been set.')
        self.__modules.append((name, path))
Esempio n. 4
0
  def __init__(self, name, command_root_directory,
               allow_non_existing_modules=False,
               logs_dir=config.Paths().logs_dir, version_func=None,
               known_error_handler=None):
    """Initialize Calliope.

    Args:
      name: str, The name of the top level command, used for nice error
        reporting.
      command_root_directory: str, The path to the directory containing the main
        CLI module.
      allow_non_existing_modules: True to allow extra module directories to not
        exist, False to raise an exception if a module does not exist.
      logs_dir: str, The path to the root directory to store logs in, or None
        for no log files.
      version_func: func, A function to call for a top-level -v and
        --version flag. If None, no flags will be available.
      known_error_handler: f(x)->None, A function to call when an known error is
        handled. It takes a single argument that is the exception.

    Raises:
      backend.LayoutException: If no command root directory is given.
    """
    self.__name = name
    self.__command_root_directory = command_root_directory
    if not self.__command_root_directory:
      raise backend.LayoutException(
          'You must specify a command root directory.')

    self.__allow_non_existing_modules = allow_non_existing_modules

    self.__logs_dir = logs_dir
    self.__version_func = version_func
    self.__known_errror_handler = known_error_handler

    self.__pre_run_hooks = []
    self.__post_run_hooks = []

    self.__modules = []
    self.__missing_components = {}
    self.__release_tracks = {}
Esempio n. 5
0
  def __LoadCLIFromGroups(self):
    """Load the CLI from a command directory.

    Returns:
      CLI, The generated CLI tool.
    """
    top_group = self.__LoadTopGroup(
        self.__GetGroupInfo(
            module_directory=self.__command_root_directory, module_path=None,
            allow_non_existing_modules=False, exception_if_present=None,
            is_top_group=True))
    self.__AddBuiltinGlobalFlags(top_group)

    for module_dot_path, module_dir in self.__modules:
      try:
        match = CLILoader.PATH_RE.match(module_dot_path)
        root, name = match.group(1, 2)
        parent_group = self.__FindParentGroup(top_group, root)
        exception_if_present = None
        if not parent_group:
          exception_if_present = backend.LayoutException(
              'Root [{root}] for command group [{group}] does not exist.'
              .format(root=root, group=name))

        path_list = module_dot_path.split('.')
        group_info = self.__GetGroupInfo(
            module_directory=module_dir, module_path=path_list,
            allow_non_existing_modules=self.__allow_non_existing_modules,
            exception_if_present=exception_if_present, is_top_group=False)

        if group_info:
          parent_group.AddSubGroup(group_info)
      except backend.CommandLoadFailure as e:
        log.exception(e)

    cli = self.__MakeCLI(top_group)

    return cli
Esempio n. 6
0
  def Generate(self):
    """Uses the registered information to generate the CLI tool.

    Returns:
      CLI, The generated CLI tool.
    """
    # The root group of the CLI.
    top_group = self.__LoadTopGroup(
        self.__GetGroupInfo(
            module_directory=self.__command_root_directory, name=self.__name,
            release_track=calliope_base.ReleaseTrack.GA,
            allow_non_existing_modules=False, exception_if_present=None))
    self.__AddBuiltinGlobalFlags(top_group)

    # Sub groups for each alternate release track.
    loaded_release_tracks = dict([(calliope_base.ReleaseTrack.GA, top_group)])
    track_names = set(track.prefix for track in self.__release_tracks.keys())
    for track, (module_dir, component) in self.__release_tracks.iteritems():
      group_info = self.__GetGroupInfo(
          module_directory=module_dir, name=track.prefix, release_track=track,
          allow_non_existing_modules=self.__allow_non_existing_modules,
          exception_if_present=None)
      if group_info:
        # Add the release track sub group into the top group.
        top_group.AddSubGroup(group_info)
        track_group = top_group.LoadSubElement(track.prefix, allow_empty=True)
        # Copy all the root elements of the top group into the release group.
        top_group.CopyAllSubElementsTo(track_group, ignore=track_names)
        loaded_release_tracks[track] = track_group
      elif component:
        self.__missing_components[track.prefix] = component

    # Load the normal set of registered sub groups.
    for module_dot_path, module_dir, component in self.__modules:
      match = CLILoader.PATH_RE.match(module_dot_path)
      root, name = match.group(1, 2)
      try:
        # Mount each registered sub group under each release track that exists.
        for track, track_root_group in loaded_release_tracks.iteritems():
          parent_group = self.__FindParentGroup(track_root_group, root)
          exception_if_present = None
          if not parent_group:
            if track != calliope_base.ReleaseTrack.GA:
              # Don't error mounting sub groups if the parent group can't be
              # found unless this is for the GA group.  The GA should always be
              # there, but for alternate release channels, the parent group
              # might not be enabled for that particular release channel, so it
              # is valid to not exist.
              continue
            exception_if_present = backend.LayoutException(
                'Root [{root}] for command group [{group}] does not exist.'
                .format(root=root, group=name))

          group_name = module_dot_path.split('.')[-1]
          group_info = self.__GetGroupInfo(
              module_directory=module_dir, name=group_name,
              release_track=(parent_group.ReleaseTrack(for_help=False)
                             if parent_group else None),
              allow_non_existing_modules=self.__allow_non_existing_modules,
              exception_if_present=exception_if_present)

          if group_info:
            parent_group.AddSubGroup(group_info)
          elif component:
            prefix = track.prefix + '.' if track.prefix else ''
            self.__missing_components[prefix + module_dot_path] = component
      except backend.CommandLoadFailure as e:
        log.exception(e)

    cli = self.__MakeCLI(top_group)

    return cli