Пример #1
0
  def __init__(self, env, config, known_scope_infos, args=sys.argv, bootstrap_option_values=None):
    """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get[list](section, name, default=)).
    :param known_scope_infos: ScopeInfos for all scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param bootstrap_option_values: An optional namespace containing the values of bootstrap
           options. We can use these values when registering other options.
    """
    # We need parsers for all the intermediate scopes, so inherited option values
    # can propagate through them.
    complete_known_scope_infos = self.complete_scopes(known_scope_infos)
    splitter = ArgSplitter(complete_known_scope_infos)
    self._goals, self._scope_to_flags, self._target_specs, self._passthru, self._passthru_owner = \
      splitter.split_args(args)

    if bootstrap_option_values:
      target_spec_files = bootstrap_option_values.target_spec_files
      if target_spec_files:
        for spec in target_spec_files:
          with open(spec) as f:
            self._target_specs.extend(filter(None, [line.strip() for line in f]))

    self._help_request = splitter.help_request

    self._parser_hierarchy = ParserHierarchy(env, config, complete_known_scope_infos)
    self._values_by_scope = {}  # Arg values, parsed per-scope on demand.
    self._bootstrap_option_values = bootstrap_option_values
    self._known_scopes = set([s[0] for s in known_scope_infos])
Пример #2
0
  def create(cls, env, config, known_scope_infos, args=None, bootstrap_option_values=None):
    """Create an Options instance.

    :param env: a dict of environment variables.
    :param :class:`pants.option.config.Config` config: data from a config file.
    :param known_scope_infos: ScopeInfos for all scopes that may be encountered.
    :param args: a list of cmd-line args; defaults to `sys.argv` if None is supplied.
    :param bootstrap_option_values: An optional namespace containing the values of bootstrap
           options. We can use these values when registering other options.
    """
    # We need parsers for all the intermediate scopes, so inherited option values
    # can propagate through them.
    complete_known_scope_infos = cls.complete_scopes(known_scope_infos)
    splitter = ArgSplitter(complete_known_scope_infos)
    args = sys.argv if args is None else args
    goals, scope_to_flags, target_specs, passthru, passthru_owner, unknown_scopes = splitter.split_args(args)

    option_tracker = OptionTracker()

    if bootstrap_option_values:
      target_spec_files = bootstrap_option_values.target_spec_files
      if target_spec_files:
        for spec in target_spec_files:
          with open(spec, 'r') as f:
            target_specs.extend([line for line in [line.strip() for line in f] if line])

    help_request = splitter.help_request

    parser_hierarchy = ParserHierarchy(env, config, complete_known_scope_infos, option_tracker)
    bootstrap_option_values = bootstrap_option_values
    known_scope_to_info = {s.scope: s for s in complete_known_scope_infos}
    return cls(goals, scope_to_flags, target_specs, passthru, passthru_owner, help_request,
               parser_hierarchy, bootstrap_option_values, known_scope_to_info,
               option_tracker, unknown_scopes)
Пример #3
0
  def __init__(self, env, config, known_scopes, args=sys.argv, bootstrap_option_values=None):
    """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get[list](section, name, default=)).
    :param known_scopes: a list of all possible scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param bootstrap_option_values: An optional namespace containing the values of bootstrap
           options. We can use these values when registering other options.
    """
    splitter = ArgSplitter(known_scopes)
    self._goals, self._scope_to_flags, self._target_specs, self._passthru, self._passthru_owner = \
      splitter.split_args(args)

    if bootstrap_option_values:
      target_spec_files = bootstrap_option_values.target_spec_files
      if target_spec_files:
        for spec in target_spec_files:
          with open(spec) as f:
            self._target_specs.extend(filter(None, [line.strip() for line in f]))

    self._help_request = splitter.help_request
    self._parser_hierarchy = ParserHierarchy(env, config, known_scopes, self._help_request)
    self._values_by_scope = {}  # Arg values, parsed per-scope on demand.
    self._bootstrap_option_values = bootstrap_option_values
    self._known_scopes = set(known_scopes)
Пример #4
0
    def create(
        cls,
        env: Mapping[str, str],
        config: Config,
        known_scope_infos: Iterable[ScopeInfo],
        args: Optional[Sequence[str]] = None,
        bootstrap_option_values: Optional[OptionValueContainer] = None,
    ) -> "Options":
        """Create an Options instance.

        :param env: a dict of environment variables.
        :param config: data from a config file.
        :param known_scope_infos: ScopeInfos for all scopes that may be encountered.
        :param args: a list of cmd-line args; defaults to `sys.argv` if None is supplied.
        :param bootstrap_option_values: An optional namespace containing the values of bootstrap
               options. We can use these values when registering other options.
        """
        # We need parsers for all the intermediate scopes, so inherited option values
        # can propagate through them.
        complete_known_scope_infos = cls.complete_scopes(known_scope_infos)
        splitter = ArgSplitter(complete_known_scope_infos)
        args = sys.argv if args is None else args
        split_args = splitter.split_args(args)

        if split_args.passthru and len(split_args.goals) > 1:
            raise cls.AmbiguousPassthroughError(
                f"Specifying multiple goals (in this case: {split_args.goals}) "
                "along with passthrough args (args after `--`) is ambiguous.\n"
                "Try either specifying only a single goal, or passing the passthrough args "
                "directly to the relevant consumer via its associated flags."
            )

        option_tracker = OptionTracker()

        if bootstrap_option_values:
            spec_files = bootstrap_option_values.spec_files
            if spec_files:
                for spec_file in spec_files:
                    with open(spec_file, "r") as f:
                        split_args.specs.extend(
                            [line for line in [line.strip() for line in f] if line]
                        )

        help_request = splitter.help_request

        parser_hierarchy = ParserHierarchy(env, config, complete_known_scope_infos, option_tracker)
        known_scope_to_info = {s.scope: s for s in complete_known_scope_infos}
        return cls(
            goals=split_args.goals,
            scope_to_flags=split_args.scope_to_flags,
            specs=split_args.specs,
            passthru=split_args.passthru,
            help_request=help_request,
            parser_hierarchy=parser_hierarchy,
            bootstrap_option_values=bootstrap_option_values,
            known_scope_to_info=known_scope_to_info,
            option_tracker=option_tracker,
            unknown_scopes=split_args.unknown_scopes,
        )
Пример #5
0
  def __init__(self, env, config, known_scopes, args=sys.argv, bootstrap_option_values=None):
    """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get[list](section, name, default=)).
    :param known_scopes: a list of all possible scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param bootstrap_option_values: An optional namespace containing the values of bootstrap
           options. We can use these values when registering other options.
    """
    splitter = ArgSplitter(known_scopes)
    self._goals, self._scope_to_flags, self._target_specs, self._passthru, self._passthru_owner = \
      splitter.split_args(args)
    self._is_help = splitter.is_help
    self._parser_hierarchy = ParserHierarchy(env, config, known_scopes)
    self._values_by_scope = {}  # Arg values, parsed per-scope on demand.
    self._bootstrap_option_values = bootstrap_option_values
    self._known_scopes = set(known_scopes)
Пример #6
0
    def create(
        cls,
        env,
        config,
        known_scope_infos,
        args=None,
        bootstrap_option_values=None,
        option_tracker=None,
    ):
        """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get[list](section, name, default=)).
    :param known_scope_infos: ScopeInfos for all scopes that may be encountered.
    :param args: a list of cmd-line args; defaults to `sys.argv` if None is supplied.
    :param bootstrap_option_values: An optional namespace containing the values of bootstrap
           options. We can use these values when registering other options.
    :param :class:`pants.option.option_tracker.OptionTracker` option_tracker: option tracker
           instance to record how option values were assigned.
    """
        # We need parsers for all the intermediate scopes, so inherited option values
        # can propagate through them.
        complete_known_scope_infos = cls.complete_scopes(known_scope_infos)
        splitter = ArgSplitter(complete_known_scope_infos)
        args = sys.argv if args is None else args
        goals, scope_to_flags, target_specs, passthru, passthru_owner = splitter.split_args(
            args)

        if not option_tracker:
            raise cls.OptionTrackerRequiredError()

        if bootstrap_option_values:
            target_spec_files = bootstrap_option_values.target_spec_files
            if target_spec_files:
                for spec in target_spec_files:
                    with open(spec) as f:
                        target_specs.extend(
                            filter(None, [line.strip() for line in f]))

        help_request = splitter.help_request

        parser_hierarchy = ParserHierarchy(env, config,
                                           complete_known_scope_infos,
                                           option_tracker)
        values_by_scope = {}  # Arg values, parsed per-scope on demand.
        bootstrap_option_values = bootstrap_option_values
        known_scope_to_info = {s.scope: s for s in complete_known_scope_infos}
        return cls(goals, scope_to_flags, target_specs, passthru,
                   passthru_owner, help_request, parser_hierarchy,
                   values_by_scope, bootstrap_option_values,
                   known_scope_to_info, option_tracker)
Пример #7
0
    def __init__(self,
                 env,
                 config,
                 known_scopes,
                 args=sys.argv,
                 legacy_parser=None):
        """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get(section, name, default=)).
    :param known_scopes: a list of all possible scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param legacy_parser: optional instance of optparse.OptionParser, used to register and access
           the old-style flags during migration.
    """
        splitter = ArgSplitter(known_scopes)
        self._scope_to_flags, self._target_specs = splitter.split_args(args)
        self._is_help = splitter.is_help
        self._parser_hierarchy = ParserHierarchy(env, config, known_scopes,
                                                 legacy_parser)
        self._legacy_parser = legacy_parser  # Old-style options, used temporarily during transition.
        self._legacy_values = None  # Values parsed from old-stype options.
        self._values_by_scope = {}  # Arg values, parsed per-scope on demand.
Пример #8
0
  def __init__(self, env, config, known_scopes, args=sys.argv, legacy_parser=None):
    """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get(section, name, default=)).
    :param known_scopes: a list of all possible scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param legacy_parser: optional instance of optparse.OptionParser, used to register and access
           the old-style flags during migration.
    """
    splitter = ArgSplitter(known_scopes)
    self._scope_to_flags, self._target_specs, self._passthru = splitter.split_args(args)
    self._is_help = splitter.is_help
    self._parser_hierarchy = ParserHierarchy(env, config, known_scopes, legacy_parser)
    self._legacy_parser = legacy_parser  # Old-style options, used temporarily during transition.
    self._legacy_values = None  # Values parsed from old-stype options.
    self._values_by_scope = {}  # Arg values, parsed per-scope on demand.
Пример #9
0
class Options(object):
  """The outward-facing API for interacting with options.

  Supports option registration and fetching option values.

  Examples:

  The value in global scope of option '--foo-bar' (registered in global scope) will be selected
  in the following order:
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the PANTS_FOO_BAR environment variable.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in global scope) will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the PANTS_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in scope 'compile') will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini
      (because of automatic config file fallback to that section).
    - The hard-coded value provided at registration time.
    - None.
  """
  GLOBAL_SCOPE = GLOBAL_SCOPE

  # Custom option types. You can specify these with type= when registering options.

  # A dict-typed option.
  dict = staticmethod(custom_types.dict_type)

  # A list-typed option. Note that this is different than an action='append' option:
  # An append option will append the cmd-line values to the default. A list-typed option
  # will replace the default with the cmd-line value.
  list = staticmethod(custom_types.list_type)


  def __init__(self, env, config, known_scopes, args=sys.argv, bootstrap_option_values=None):
    """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get[list](section, name, default=)).
    :param known_scopes: a list of all possible scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param bootstrap_option_values: An optional namespace containing the values of bootstrap
           options. We can use these values when registering other options.
    """
    splitter = ArgSplitter(known_scopes)
    self._goals, self._scope_to_flags, self._target_specs, self._passthru, self._passthru_owner = \
      splitter.split_args(args)

    if bootstrap_option_values:
      target_spec_files = bootstrap_option_values.target_spec_files
      if target_spec_files:
        for spec in target_spec_files:
          with open(spec) as f:
            self._target_specs.extend(filter(None, [line.strip() for line in f]))

    self._help_request = splitter.help_request
    self._parser_hierarchy = ParserHierarchy(env, config, known_scopes, self._help_request)
    self._values_by_scope = {}  # Arg values, parsed per-scope on demand.
    self._bootstrap_option_values = bootstrap_option_values
    self._known_scopes = set(known_scopes)

  @property
  def target_specs(self):
    """The targets to operate on."""
    return self._target_specs

  @property
  def goals(self):
    """The requested goals, in the order specified on the cmd line."""
    return self._goals

  def is_known_scope(self, scope):
    """Whether the given scope is known by this instance."""
    return scope in self._known_scopes

  def passthru_args_for_scope(self, scope):
    # Passthru args "belong" to the last scope mentioned on the command-line.

    # Note: If that last scope is a goal, we allow all tasks in that goal to access the passthru
    # args. This is to allow the more intuitive
    # pants run <target> -- <passthru args>
    # instead of requiring
    # pants run.py <target> -- <passthru args>.
    #
    # However note that in the case where multiple tasks run in the same goal, e.g.,
    # pants test <target> -- <passthru args>
    # Then, e.g., both junit and pytest will get the passthru args even though the user probably
    # only intended them to go to one of them. If the wrong one is not a no-op then the error will
    # be unpredictable. However this is  not a common case, and can be circumvented with an
    # explicit test.pytest or test.junit scope.
    if (scope and self._passthru_owner and scope.startswith(self._passthru_owner) and
          (len(scope) == len(self._passthru_owner) or scope[len(self._passthru_owner)] == '.')):
      return self._passthru
    else:
      return []

  def register(self, scope, *args, **kwargs):
    """Register an option in the given scope, using argparse params."""
    self.get_parser(scope).register(*args, **kwargs)

  def register_global(self, *args, **kwargs):
    """Register an option in the global scope, using argparse params."""
    self.register(GLOBAL_SCOPE, *args, **kwargs)

  def registration_function_for_global_scope(self):
    """Returns a function for registering argparse args on the global scope."""
    return self.registration_function_for_scope(GLOBAL_SCOPE)

  def registration_function_for_scope(self, scope):
    """Returns a function for registering argparse args on the given scope."""
    # TODO(benjy): Make this an instance of a class that implements __call__, so we can
    # docstring it, and so it's less weird than attatching properties to a function.
    def register(*args, **kwargs):
      self.register(scope, *args, **kwargs)
    # Clients can access the bootstrap option values as register.bootstrap.
    register.bootstrap = self.bootstrap_option_values()
    # Clients can access the scope as register.scope.
    register.scope = scope
    return register

  def get_parser(self, scope):
    """Returns the parser for the given scope, so code can register on it directly."""
    return self._parser_hierarchy.get_parser_by_scope(scope)

  def get_global_parser(self):
    """Returns the parser for the global scope, so code can register on it directly."""
    return self.get_parser(GLOBAL_SCOPE)

  def for_scope(self, scope):
    """Return the option values for the given scope.

    Values are attributes of the returned object, e.g., options.foo.
    Computed lazily per scope.
    """
    # Short-circuit, if already computed.
    if scope in self._values_by_scope:
      return self._values_by_scope[scope]

    # First get enclosing scope's option values, if any.
    if scope == GLOBAL_SCOPE:
      values = OptionValueContainer()
    else:
      values = copy.deepcopy(self.for_scope(scope.rpartition('.')[0]))

    # Now add our values.
    flags_in_scope = self._scope_to_flags.get(scope, [])
    self._parser_hierarchy.get_parser_by_scope(scope).parse_args(flags_in_scope, values)
    self._values_by_scope[scope] = values
    return values

  def __getitem__(self, scope):
    # TODO(John Sirois): Mainly supports use of dict<str, dict<str, str>> for mock options in tests,
    # Consider killing if tests consolidate on using TestOptions instead of the raw dicts.
    return self.for_scope(scope)

  def bootstrap_option_values(self):
    """Return the option values for bootstrap options.

    General code can also access these values in the global scope.  But option registration code
    cannot, hence this special-casing of this small set of options.
    """
    return self._bootstrap_option_values

  def for_global_scope(self):
    """Return the option values for the global scope."""
    return self.for_scope(GLOBAL_SCOPE)

  def print_help_if_requested(self):
    """If help was requested, print it and return True.

    Otherwise return False.
    """
    if self._help_request:
      if self._help_request.version:
        print(pants_version())
      else:
        self._print_help()
      return True
    else:
      return False

  def _print_help(self):
    """Print a help screen, followed by an optional message.

    Note: Ony useful if called after options have been registered.
    """
    def _maybe_help(scope):
      s = self._format_help_for_scope(scope)
      if s != '':  # Avoid printing scope name for scope with empty options.
        print(scope)
        for line in s.split('\n'):
          if line != '':  # Avoid superfluous blank lines for empty strings.
            print('  {0}'.format(line))

    show_all_help = self._help_request and self._help_request.all_scopes
    goals = (Goal.all() if show_all_help else [Goal.by_name(goal_name) for goal_name in self.goals])
    if goals:
      for goal in goals:
        if not goal.ordered_task_names():
          print('\nUnknown goal: {}'.format(goal.name))
        else:
          print('\n{0}: {1}\n'.format(goal.name, goal.description))
          for scope in goal.known_scopes():
            _maybe_help(scope)
    else:
      print(pants_release())
      print('\nUsage:')
      print('  ./pants [option ...] [goal ...] [target...]  Attempt the specified goals.')
      print('  ./pants help                                 Get help.')
      print('  ./pants help [goal]                          Get help for a goal.')
      print('  ./pants help-advanced [goal]                 Get help for a goal\'s advanced options.')
      print('  ./pants help-all                             Get help for all goals.')
      print('  ./pants goals                                List all installed goals.')
      print('')
      print('  [target] accepts two special forms:')
      print('    dir:  to include all targets in the specified directory.')
      print('    dir:: to include all targets found recursively under the directory.')
      print('\nFriendly docs:\n  http://pantsbuild.github.io/')

    if show_all_help or not goals:
      print('\nGlobal options:')
      print(self.get_global_parser().format_help())

  def _format_help_for_scope(self, scope):
    """Generate a help message for options at the specified scope."""
    return self.get_parser(scope).format_help()
Пример #10
0
class Options(object):
  """The outward-facing API for interacting with options.

  Supports option registration and fetching option values.

  Examples:

  The value in global scope of option '--foo-bar' (registered in global scope) will be selected
  in the following order:
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in global scope) will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in scope 'compile') will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini
      (because of automatic config file fallback to that section).
    - The hard-coded value provided at registration time.
    - None.
  """
  def __init__(self, env, config, known_scopes, args=sys.argv, legacy_parser=None):
    """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get(section, name, default=)).
    :param known_scopes: a list of all possible scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param legacy_parser: optional instance of optparse.OptionParser, used to register and access
           the old-style flags during migration.
    """
    splitter = ArgSplitter(known_scopes)
    self._scope_to_flags, self._target_specs, self._passthru = splitter.split_args(args)
    self._is_help = splitter.is_help
    self._parser_hierarchy = ParserHierarchy(env, config, known_scopes, legacy_parser)
    self._legacy_parser = legacy_parser  # Old-style options, used temporarily during transition.
    self._legacy_values = None  # Values parsed from old-stype options.
    self._values_by_scope = {}  # Arg values, parsed per-scope on demand.

  @property
  def target_specs(self):
    """The targets to operate on."""
    return self._target_specs

  @property
  def goals(self):
    """The requested goals, in the order specified on the cmd-line."""
    return OrderedSet([g.partition('.')[0] for g in self._scope_to_flags.keys() if g])

  @property
  def is_help(self):
    """Whether the command line indicates a request for help."""
    return self._is_help

  def set_legacy_values(self, legacy_values):
    """Override the values with those parsed from legacy flags."""
    self._legacy_values = legacy_values

  def format_global_help(self, legacy=False):
    """Generate a help message for global options."""
    return self.get_global_parser().format_help(legacy=legacy)

  def format_help(self, scope, legacy=False):
    """Generate a help message for options at the specified scope."""
    return self.get_parser(scope).format_help(legacy=legacy)

  def register(self, scope, *args, **kwargs):
    """Register an option in the given scope, using argparse params."""
    self.get_parser(scope).register(*args, **kwargs)

  def register_global(self, *args, **kwargs):
    """Register an option in the global scope, using argparse params."""
    self.register(GLOBAL_SCOPE, *args, **kwargs)

  def get_parser(self, scope):
    """Returns the parser for the given scope, so code can register on it directly."""
    return self._parser_hierarchy.get_parser_by_scope(scope)

  def get_global_parser(self):
    """Returns the parser for the global scope, so code can register on it directly."""
    return self.get_parser(GLOBAL_SCOPE)

  def for_scope(self, scope):
    """Return the option values for the given scope.

    Values are attributes of the returned object, e.g., options.foo.
    Computed lazily per scope.
    """
    # Short-circuit, if already computed.
    if scope in self._values_by_scope:
      return self._values_by_scope[scope]

    # First get enclosing scope's option values, if any.
    if scope == GLOBAL_SCOPE:
      values = OptionValueContainer()
      if self._legacy_values:
        values.update(vars(self._legacy_values))  # Proxy any legacy option values.
    else:
      values = copy.deepcopy(self.for_scope(scope.rpartition('.')[0]))

    # Now add our values.
    flags_in_scope = self._scope_to_flags.get(scope, [])
    self._parser_hierarchy.get_parser_by_scope(scope).parse_args(flags_in_scope, values)
    self._values_by_scope[scope] = values
    return values

  def for_global_scope(self):
    """Return the option values for the global scope."""
    return self.for_scope(GLOBAL_SCOPE)

  def print_help(self, msg=None, goals=None, legacy=False):
    """Print a help screen, followed by an optional message.

    Note: Ony useful if called after options have been registered.
    """
    def _maybe_help(scope):
      s = self.format_help(scope, legacy=legacy)
      if s != '':  # Avoid superfluous blank lines for empty strings.
        print(s)

    goals = goals or self.goals
    if goals:
      for goal_name in goals:
        goal = Goal.by_name(goal_name)
        if not goal.ordered_task_names():
          print('\nUnknown goal: %s' % goal_name)
        else:
          print('\n{0}: {1}\n'.format(goal.name, goal.description))
          for scope in goal.known_scopes():
            _maybe_help(scope)
    else:
      print(pants_release())
      print('\nUsage:')
      print('  ./pants [option ...] [goal ...] [target...]  Attempt the specified goals.')
      print('  ./pants help                                 Get help.')
      print('  ./pants help [goal]                          Get help for the specified goal.')
      print('  ./pants goal goals                           List all installed goals.')
      print('')
      print('  [target] accepts two special forms:')
      print('    dir:  to include all targets in the specified directory.')
      print('    dir:: to include all targets found recursively under the directory.')

      print('\nFriendly docs:\n  http://pantsbuild.github.io/')

      print('\nGlobal options:')
      print(self.format_global_help())

    if msg is not None:
      print(msg)
Пример #11
0
class Options(object):
    """The outward-facing API for interacting with options.

  Supports option registration and fetching option values.

  Examples:

  The value in global scope of option '--foo-bar' (registered in global scope) will be selected
  in the following order:
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in global scope) will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in scope 'compile') will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini
      (because of automatic config file fallback to that section).
    - The hard-coded value provided at registration time.
    - None.
  """

    # Custom option types. You can specify these with type= when registering options.

    # A dict-typed option.
    dict = staticmethod(custom_types.dict_type)

    # A list-typed option. Note that this is different than an action='append' option:
    # An append option will append the cmd-line values to the default. A list-typed option
    # will replace the default with the cmd-line value.
    list = staticmethod(custom_types.list_type)

    def __init__(self,
                 env,
                 config,
                 known_scopes,
                 args=sys.argv,
                 bootstrap_option_values=None):
        """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get[list](section, name, default=)).
    :param known_scopes: a list of all possible scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param bootstrap_option_values: An optional namespace containing the values of bootstrap
           options. We can use these values when registering other options.
    """
        splitter = ArgSplitter(known_scopes)
        self._goals, self._scope_to_flags, self._target_specs, self._passthru, self._passthru_owner = \
          splitter.split_args(args)
        self._is_help = splitter.is_help
        self._parser_hierarchy = ParserHierarchy(env, config, known_scopes)
        self._values_by_scope = {}  # Arg values, parsed per-scope on demand.
        self._bootstrap_option_values = bootstrap_option_values

    @property
    def target_specs(self):
        """The targets to operate on."""
        return self._target_specs

    @property
    def goals(self):
        """The requested goals, in the order specified on the cmd line."""
        return self._goals

    def passthru_args_for_scope(self, scope):
        # Passthru args "belong" to the last scope mentioned on the command-line.

        # Note: If that last scope is a goal, we allow all tasks in that goal to access the passthru
        # args. This is to allow the more intuitive
        # pants run <target> -- <passthru args>
        # instead of requiring
        # pants run.py <target> -- <passthru args>.
        #
        # However note that in the case where multiple tasks run in the same goal, e.g.,
        # pants test <target> -- <passthru args>
        # Then, e.g., both junit and pytest will get the passthru args even though the user probably
        # only intended them to go to one of them. If the wrong one is not a no-op then the error will
        # be unpredictable. However this is  not a common case, and can be circumvented with an
        # explicit test.pytest or test.junit scope.
        if (scope and self._passthru_owner
                and scope.startswith(self._passthru_owner)
                and (len(scope) == len(self._passthru_owner)
                     or scope[len(self._passthru_owner)] == '.')):
            return self._passthru
        else:
            return []

    @property
    def is_help(self):
        """Whether the command line indicates a request for help."""
        return self._is_help

    def format_global_help(self):
        """Generate a help message for global options."""
        return self.get_global_parser().format_help()

    def format_help(self, scope):
        """Generate a help message for options at the specified scope."""
        return self.get_parser(scope).format_help()

    def register(self, scope, *args, **kwargs):
        """Register an option in the given scope, using argparse params."""
        self.get_parser(scope).register(*args, **kwargs)

    def register_global(self, *args, **kwargs):
        """Register an option in the global scope, using argparse params."""
        self.register(GLOBAL_SCOPE, *args, **kwargs)

    def get_parser(self, scope):
        """Returns the parser for the given scope, so code can register on it directly."""
        return self._parser_hierarchy.get_parser_by_scope(scope)

    def get_global_parser(self):
        """Returns the parser for the global scope, so code can register on it directly."""
        return self.get_parser(GLOBAL_SCOPE)

    def for_scope(self, scope):
        """Return the option values for the given scope.

    Values are attributes of the returned object, e.g., options.foo.
    Computed lazily per scope.
    """
        # Short-circuit, if already computed.
        if scope in self._values_by_scope:
            return self._values_by_scope[scope]

        # First get enclosing scope's option values, if any.
        if scope == GLOBAL_SCOPE:
            values = OptionValueContainer()
        else:
            values = copy.deepcopy(self.for_scope(scope.rpartition('.')[0]))

        # Now add our values.
        flags_in_scope = self._scope_to_flags.get(scope, [])
        self._parser_hierarchy.get_parser_by_scope(scope).parse_args(
            flags_in_scope, values)
        self._values_by_scope[scope] = values
        return values

    def bootstrap_option_values(self):
        """Return the option values for bootstrap options.

    General code can also access these values in the global scope.  But option registration code
    cannot, hence this special-casing of this small set of options.
    """
        return self._bootstrap_option_values

    def for_global_scope(self):
        """Return the option values for the global scope."""
        return self.for_scope(GLOBAL_SCOPE)

    def print_help(self, msg=None, goals=None):
        """Print a help screen, followed by an optional message.

    Note: Ony useful if called after options have been registered.
    """
        def _maybe_help(scope):
            s = self.format_help(scope)
            if s != '':  # Avoid printing scope name for scope with empty options.
                print(scope)
                for line in s.split('\n'):
                    if line != '':  # Avoid superfluous blank lines for empty strings.
                        print('  {0}'.format(line))

        goals = goals or self.goals
        if goals:
            for goal_name in goals:
                goal = Goal.by_name(goal_name)
                if not goal.ordered_task_names():
                    print('\nUnknown goal: %s' % goal_name)
                else:
                    print('\n{0}: {1}\n'.format(goal.name, goal.description))
                    for scope in goal.known_scopes():
                        _maybe_help(scope)
        else:
            print(pants_release())
            print('\nUsage:')
            print(
                '  ./pants [option ...] [goal ...] [target...]  Attempt the specified goals.'
            )
            print('  ./pants help                                 Get help.')
            print(
                '  ./pants help [goal]                          Get help for the specified goal.'
            )
            print(
                '  ./pants goal goals                           List all installed goals.'
            )
            print('')
            print('  [target] accepts two special forms:')
            print(
                '    dir:  to include all targets in the specified directory.')
            print(
                '    dir:: to include all targets found recursively under the directory.'
            )

            print('\nFriendly docs:\n  http://pantsbuild.github.io/')

            print('\nGlobal options:')
            print(self.format_global_help())

        if msg is not None:
            print(msg)
Пример #12
0
class Options(object):
  """The outward-facing API for interacting with options.

  Supports option registration and fetching option values.

  Examples:

  The value in global scope of option '--foo-bar' (registered in global scope) will be selected
  in the following order:
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the PANTS_FOO_BAR environment variable.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in global scope) will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the PANTS_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in scope 'compile') will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini
      (because of automatic config file fallback to that section).
    - The hard-coded value provided at registration time.
    - None.
  """
  GLOBAL_SCOPE = GLOBAL_SCOPE

  # Custom option types. You can specify these with type= when registering options.

  # A dict-typed option.
  dict = staticmethod(custom_types.dict_type)

  # A list-typed option. Note that this is different than an action='append' option:
  # An append option will append the cmd-line values to the default. A list-typed option
  # will replace the default with the cmd-line value.
  list = staticmethod(custom_types.list_type)

  # A list-typed option that indicates the list elements are target specs.
  target_list = staticmethod(custom_types.target_list_type)

  # A string-typed option that indicates the string is a filepath.
  file = staticmethod(custom_types.file_type)

  @classmethod
  def complete_scopes(cls, scope_infos):
    """Expand a set of scopes to include all enclosing scopes.

    E.g., if the set contains `foo.bar.baz`, ensure that it also contains `foo.bar` and `foo`.
    """
    ret = {ScopeInfo.for_global_scope()}
    for scope_info in scope_infos:
      ret.add(scope_info)

    original_scopes = {si.scope for si in scope_infos}
    for scope_info in scope_infos:
      scope = scope_info.scope
      while scope != '':
        if scope not in original_scopes:
          ret.add(ScopeInfo(scope, ScopeInfo.INTERMEDIATE))
        scope = scope.rpartition('.')[0]
    return ret

  def __init__(self, env, config, known_scope_infos, args=sys.argv, bootstrap_option_values=None):
    """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get[list](section, name, default=)).
    :param known_scope_infos: ScopeInfos for all scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param bootstrap_option_values: An optional namespace containing the values of bootstrap
           options. We can use these values when registering other options.
    """
    # We need parsers for all the intermediate scopes, so inherited option values
    # can propagate through them.
    complete_known_scope_infos = self.complete_scopes(known_scope_infos)
    splitter = ArgSplitter(complete_known_scope_infos)
    self._goals, self._scope_to_flags, self._target_specs, self._passthru, self._passthru_owner = \
      splitter.split_args(args)

    if bootstrap_option_values:
      target_spec_files = bootstrap_option_values.target_spec_files
      if target_spec_files:
        for spec in target_spec_files:
          with open(spec) as f:
            self._target_specs.extend(filter(None, [line.strip() for line in f]))

    self._help_request = splitter.help_request

    self._parser_hierarchy = ParserHierarchy(env, config, complete_known_scope_infos)
    self._values_by_scope = {}  # Arg values, parsed per-scope on demand.
    self._bootstrap_option_values = bootstrap_option_values
    self._known_scopes = set([s[0] for s in known_scope_infos])

  @property
  def target_specs(self):
    """The targets to operate on."""
    return self._target_specs

  @property
  def goals(self):
    """The requested goals, in the order specified on the cmd line."""
    return self._goals

  def is_known_scope(self, scope):
    """Whether the given scope is known by this instance."""
    return scope in self._known_scopes

  def passthru_args_for_scope(self, scope):
    # Passthru args "belong" to the last scope mentioned on the command-line.

    # Note: If that last scope is a goal, we allow all tasks in that goal to access the passthru
    # args. This is to allow the more intuitive
    # pants run <target> -- <passthru args>
    # instead of requiring
    # pants run.py <target> -- <passthru args>.
    #
    # However note that in the case where multiple tasks run in the same goal, e.g.,
    # pants test <target> -- <passthru args>
    # Then, e.g., both junit and pytest will get the passthru args even though the user probably
    # only intended them to go to one of them. If the wrong one is not a no-op then the error will
    # be unpredictable. However this is  not a common case, and can be circumvented with an
    # explicit test.pytest or test.junit scope.
    if (scope and self._passthru_owner and scope.startswith(self._passthru_owner) and
          (len(scope) == len(self._passthru_owner) or scope[len(self._passthru_owner)] == '.')):
      return self._passthru
    else:
      return []

  def register(self, scope, *args, **kwargs):
    """Register an option in the given scope, using argparse params."""
    self.get_parser(scope).register(*args, **kwargs)

  def registration_function_for_optionable(self, optionable_class):
    """Returns a function for registering argparse args on the given scope."""
    # TODO(benjy): Make this an instance of a class that implements __call__, so we can
    # docstring it, and so it's less weird than attatching properties to a function.
    def register(*args, **kwargs):
      kwargs['registering_class'] = optionable_class
      self.register(optionable_class.options_scope, *args, **kwargs)
    # Clients can access the bootstrap option values as register.bootstrap.
    register.bootstrap = self.bootstrap_option_values()
    # Clients can access the scope as register.scope.
    register.scope = optionable_class.options_scope
    return register

  def get_parser(self, scope):
    """Returns the parser for the given scope, so code can register on it directly."""
    return self._parser_hierarchy.get_parser_by_scope(scope)

  def walk_parsers(self, callback):
    self._parser_hierarchy.walk(callback)

  def for_scope(self, scope):
    """Return the option values for the given scope.

    Values are attributes of the returned object, e.g., options.foo.
    Computed lazily per scope.
    """
    # Short-circuit, if already computed.
    if scope in self._values_by_scope:
      return self._values_by_scope[scope]

    # First get enclosing scope's option values, if any.
    if scope == GLOBAL_SCOPE:
      values = OptionValueContainer()
    else:
      values = copy.deepcopy(self.for_scope(scope.rpartition('.')[0]))

    # Now add our values.
    flags_in_scope = self._scope_to_flags.get(scope, [])
    self._parser_hierarchy.get_parser_by_scope(scope).parse_args(flags_in_scope, values)
    self._values_by_scope[scope] = values
    return values

  def registration_args_iter_for_scope(self, scope):
    """Returns an iterator over the registration arguments of each option in this scope.

    See `Parser.registration_args_iter` for details.
    """
    return self._parser_hierarchy.get_parser_by_scope(scope).registration_args_iter()

  def payload_for_scope(self, scope):
    """Returns a payload representing the options for the given scope."""
    payload = Payload()
    for (name, _, kwargs) in self.registration_args_iter_for_scope(scope):
      if not kwargs.get('fingerprint', False):
        continue
      val = self.for_scope(scope)[name]
      val_type = kwargs.get('type', '')
      if val_type == Options.file:
        field = FileField(val)
      elif val_type == Options.target_list:
        field = TargetListField(val)
      else:
        field = PrimitiveField(val)
      payload.add_field(name, field)
    payload.freeze()
    return payload

  def __getitem__(self, scope):
    # TODO(John Sirois): Mainly supports use of dict<str, dict<str, str>> for mock options in tests,
    # Consider killing if tests consolidate on using TestOptions instead of the raw dicts.
    return self.for_scope(scope)

  def bootstrap_option_values(self):
    """Return the option values for bootstrap options.

    General code can also access these values in the global scope.  But option registration code
    cannot, hence this special-casing of this small set of options.
    """
    return self._bootstrap_option_values

  def for_global_scope(self):
    """Return the option values for the global scope."""
    return self.for_scope(GLOBAL_SCOPE)

  def print_help_if_requested(self):
    """If help was requested, print it and return True.

    Otherwise return False.
    """
    if self._help_request:
      if self._help_request.version:
        print(pants_version())
      else:
        self._print_help()
      return True
    else:
      return False

  def _print_help(self):
    """Print a help screen, followed by an optional message.

    Note: Ony useful if called after options have been registered.
    """
    show_all_help = self._help_request and self._help_request.all_scopes
    goals = (Goal.all() if show_all_help else [Goal.by_name(goal_name) for goal_name in self.goals])
    if goals:
      for goal in goals:
        if not goal.ordered_task_names():
          print('\nUnknown goal: {}'.format(goal.name))
        else:
          print('\n{0}: {1}\n'.format(goal.name, goal.description))
          for scope_info in goal.known_scope_infos():
            help_str = self._format_help_for_scope(scope_info.scope)
            if help_str:
              print(help_str)
    else:
      print(pants_release())
      print('\nUsage:')
      print('  ./pants [option ...] [goal ...] [target...]  Attempt the specified goals.')
      print('  ./pants help                                 Get help.')
      print('  ./pants help [goal]                          Get help for a goal.')
      print('  ./pants help-advanced [goal]                 Get help for a goal\'s advanced options.')
      print('  ./pants help-all                             Get help for all goals.')
      print('  ./pants goals                                List all installed goals.')
      print('')
      print('  [target] accepts two special forms:')
      print('    dir:  to include all targets in the specified directory.')
      print('    dir:: to include all targets found recursively under the directory.')
      print('\nFriendly docs:\n  http://pantsbuild.github.io/')

    if show_all_help or not goals:
      print(self.get_parser(GLOBAL_SCOPE).format_help('Global', self._help_request.advanced))

  def _format_help_for_scope(self, scope):
    """Generate a help message for options at the specified scope."""
    return self.get_parser(scope).format_help(scope, self._help_request.advanced)
Пример #13
0
class Options(object):
    """The outward-facing API for interacting with options.

  Supports option registration and fetching option values.

  Examples:

  The value in global scope of option '--foo-bar' (registered in global scope) will be selected
  in the following order:
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in global scope) will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the --foo-bar flag in global scope.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the PANTS_DEFAULT_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini.
    - The hard-coded value provided at registration time.
    - None.

  The value in scope 'compile.java' of option '--foo-bar' (registered in scope 'compile') will be
  selected in the following order:
    - The value of the --foo-bar flag in scope 'compile.java'.
    - The value of the --foo-bar flag in scope 'compile'.
    - The value of the PANTS_COMPILE_JAVA_FOO_BAR environment variable.
    - The value of the PANTS_COMPILE_FOO_BAR environment variable.
    - The value of the foo_bar key in the [compile.java] section of pants.ini.
    - The value of the foo_bar key in the [compile] section of pants.ini.
    - The value of the foo_bar key in the [DEFAULT] section of pants.ini
      (because of automatic config file fallback to that section).
    - The hard-coded value provided at registration time.
    - None.
  """
    def __init__(self,
                 env,
                 config,
                 known_scopes,
                 args=sys.argv,
                 legacy_parser=None):
        """Create an Options instance.

    :param env: a dict of environment variables.
    :param config: data from a config file (must support config.get(section, name, default=)).
    :param known_scopes: a list of all possible scopes that may be encountered.
    :param args: a list of cmd-line args.
    :param legacy_parser: optional instance of optparse.OptionParser, used to register and access
           the old-style flags during migration.
    """
        splitter = ArgSplitter(known_scopes)
        self._scope_to_flags, self._target_specs = splitter.split_args(args)
        self._is_help = splitter.is_help
        self._parser_hierarchy = ParserHierarchy(env, config, known_scopes,
                                                 legacy_parser)
        self._legacy_parser = legacy_parser  # Old-style options, used temporarily during transition.
        self._legacy_values = None  # Values parsed from old-stype options.
        self._values_by_scope = {}  # Arg values, parsed per-scope on demand.

    @property
    def target_specs(self):
        """The targets to operate on."""
        return self._target_specs

    @property
    def goals(self):
        """The requested goals."""
        # TODO: Order them in some way? We don't know anything about the topological
        # order here, but it would be nice to, e.g., display help in that order.
        return set(
            [g.partition('.')[0] for g in self._scope_to_flags.keys() if g])

    @property
    def is_help(self):
        """Whether the command line indicates a request for help."""
        return self._is_help

    def set_legacy_values(self, legacy_values):
        """Override the values with those parsed from legacy flags."""
        self._legacy_values = legacy_values

    def format_global_help(self, legacy=False):
        """Generate a help message for global options."""
        return self.get_global_parser().format_help(legacy=legacy)

    def format_help(self, scope, legacy=False):
        """Generate a help message for options at the specified scope."""
        return self.get_parser(scope).format_help(legacy=legacy)

    def register(self, scope, *args, **kwargs):
        """Register an option in the given scope, using argparse params."""
        self.get_parser(scope).register(*args, **kwargs)

    def register_global(self, *args, **kwargs):
        """Register an option in the global scope, using argparse params."""
        self.register(GLOBAL_SCOPE, *args, **kwargs)

    def get_parser(self, scope):
        """Returns the parser for the given scope, so code can register on it directly."""
        return self._parser_hierarchy.get_parser_by_scope(scope)

    def get_global_parser(self):
        """Returns the parser for the global scope, so code can register on it directly."""
        return self.get_parser(GLOBAL_SCOPE)

    def for_scope(self, scope):
        """Return the option values for the given scope.

    Values are attributes of the returned object, e.g., options.foo.
    Computed lazily per scope.
    """
        # Short-circuit, if already computed.
        if scope in self._values_by_scope:
            return self._values_by_scope[scope]

        # First get enclosing scope's option values, if any.
        if scope == GLOBAL_SCOPE:
            values = OptionValueContainer()
            if self._legacy_values:
                values.update(vars(
                    self._legacy_values))  # Proxy any legacy option values.
        else:
            values = copy.copy(self.for_scope(scope.rpartition('.')[0]))

        # Now add our values.
        try:
            flags_in_scope = self._scope_to_flags.get(scope, [])
            self._parser_hierarchy.get_parser_by_scope(scope).parse_args(
                flags_in_scope, values)
            self._values_by_scope[scope] = values
            return values
        except ParseError as e:
            self.print_help(str(e))
            sys.exit(1)

    def for_global_scope(self):
        """Return the option values for the global scope."""
        return self.for_scope(GLOBAL_SCOPE)

    def print_help(self, msg=None, goals=None, legacy=False):
        """Print a help screen, followed by an optional message.

    Note: Ony useful if called after options have been registered.
    """
        def _maybe_print(s):
            if s != '':  # Avoid superfluous blank lines for empty strings.
                print(s)

        goals = goals or self.goals
        if goals:
            for goal_name in goals:
                goal = Goal.by_name(goal_name)
                if not goal.ordered_task_names():
                    print('\nUnknown goal: %s' % goal_name)
                else:
                    _maybe_print(
                        self.format_help('%s' % goal.name, legacy=legacy))
                    for task_name in goal.ordered_task_names():
                        if task_name != goal.name:  # Otherwise we registered on the goal scope.
                            scope = '%s.%s' % (goal.name, task_name)
                            _maybe_print(self.format_help(scope,
                                                          legacy=legacy))
        else:
            print(pants_release())
            print('\nUsage:')
            print(
                '  ./pants [option ...] [goal ...] [target...]  Attempt the specified goals.'
            )
            print('  ./pants help                                 Get help.')
            print(
                '  ./pants help [goal]                          Get help for the specified goal.'
            )
            print(
                '  ./pants goals                                List all installed goals.'
            )
            print('')
            print('  [target] accepts two special forms:')
            print(
                '    dir:  to include all targets in the specified directory.')
            print(
                '    dir:: to include all targets found recursively under the directory.'
            )

            print('\nFriendly docs:\n  http://pantsbuild.github.io/')

            print('\nGlobal options:')
            print(self.format_global_help())

        if msg is not None:
            print(msg)