Example #1
0
def create_options_for_optionables(optionables, extra_scopes=None, options=None):
    """Create a fake Options object for testing with appropriate defaults for the given optionables.

  Any scoped `options` provided will override defaults, behaving as-if set on the command line.

  :param iterable optionables: A series of `Optionable` types to register default options for.
  :param iterable extra_scopes: An optional series of extra known scopes in play.
  :param dict options: A dict of scope -> (dict of option name -> value) representing option values
                       explicitly set via the command line.
  :returns: A fake `Options` object with defaults populated for the given `optionables` and any
            explicitly set `options` overlayed.
  """
    all_options = defaultdict(dict)
    bootstrap_option_values = None

    def register_func(on_scope):
        scoped_options = all_options[on_scope]
        register = _options_registration_function(scoped_options)
        register.bootstrap = bootstrap_option_values
        register.scope = on_scope
        return register

    # TODO: This sequence is a bit repetitive of the real registration sequence.

    # Register bootstrap options and grab their default values for use in subsequent registration.
    GlobalOptionsRegistrar.register_bootstrap_options(register_func(GLOBAL_SCOPE))
    bootstrap_option_values = create_option_values(all_options[GLOBAL_SCOPE].copy())

    # Now register the full global scope options.
    GlobalOptionsRegistrar.register_options(register_func(GLOBAL_SCOPE))

    for optionable in optionables:
        optionable.register_options(register_func(optionable.options_scope))

    # Make inner scopes inherit option values from their enclosing scopes.
    all_scopes = set(all_options.keys())

    # TODO(John Sirois): Kill extra scopes one this goes in:
    #   https://github.com/pantsbuild/pants/issues/1957
    # For now we need a way for users of this utility to provide extra derived scopes out of band.
    # With #1957 resolved, the extra scopes will be embedded in the Optionable's option_scope
    # directly.
    if extra_scopes:
        all_scopes.update(extra_scopes)

    # Iterating in sorted order guarantees that we see outer scopes before inner scopes,
    # and therefore only have to inherit from our immediately enclosing scope.
    for s in sorted(all_scopes):
        if s != GLOBAL_SCOPE:
            enclosing_scope = s.rpartition(".")[0]
            opts = all_options[s]
            for key, val in all_options.get(enclosing_scope, {}).items():
                if key not in opts:  # Inner scope values override the inherited ones.
                    opts[key] = val

    if options:
        for scope, opts in options.items():
            all_options[scope].update(opts)
    return create_options(all_options)
Example #2
0
def create_options_for_optionables(optionables,
                                   options=None,
                                   options_fingerprintable=None,
                                   passthru_args=None):
    """Create a fake Options object for testing with appropriate defaults for the given optionables.

  Any scoped `options` provided will override defaults, behaving as-if set on the command line.

  :param iterable optionables: A series of `Optionable` types to register default options for.
  :param dict options: A dict of scope -> (dict of option name -> value) representing option values
                       explicitly set via the command line.
  :param dict options_fingerprintable: A dict of scope -> (dict of option name -> option type)
                                       representing the fingerprintable options
                                       and the scopes they are registered for.
  :param list passthru_args: A list of passthrough args (specified after `--` on the command line).
  :returns: A fake `Options` object with defaults populated for the given `optionables` and any
            explicitly set `options` overlayed.
  """
    all_options = defaultdict(dict)
    fingerprintable_options = defaultdict(dict)
    bootstrap_option_values = None

    if options_fingerprintable:
        for scope, opts in options_fingerprintable.items():
            fingerprintable_options[scope].update(opts)

    def register_func(on_scope):
        scoped_options = all_options[on_scope]
        scoped_fingerprintables = fingerprintable_options[on_scope]
        register = _options_registration_function(scoped_options,
                                                  scoped_fingerprintables)
        register.bootstrap = bootstrap_option_values
        register.scope = on_scope
        return register

    # TODO: This sequence is a bit repetitive of the real registration sequence.

    # Register bootstrap options and grab their default values for use in subsequent registration.
    GlobalOptionsRegistrar.register_bootstrap_options(
        register_func(GLOBAL_SCOPE))
    bootstrap_option_values = _FakeOptionValues(
        all_options[GLOBAL_SCOPE].copy())

    # Now register the full global scope options.
    GlobalOptionsRegistrar.register_options(register_func(GLOBAL_SCOPE))

    for optionable in optionables:
        optionable.register_options(register_func(optionable.options_scope))

    if options:
        for scope, opts in options.items():
            all_options[scope].update(opts)

    return create_options(all_options,
                          passthru_args=passthru_args,
                          fingerprintable_options=fingerprintable_options)
Example #3
0
def create_options_for_optionables(optionables,
                                   options=None,
                                   options_fingerprintable=None,
                                   passthru_args=None):
  """Create a fake Options object for testing with appropriate defaults for the given optionables.

  Any scoped `options` provided will override defaults, behaving as-if set on the command line.

  :param iterable optionables: A series of `Optionable` types to register default options for.
  :param dict options: A dict of scope -> (dict of option name -> value) representing option values
                       explicitly set via the command line.
  :param dict options_fingerprintable: A dict of scope -> (dict of option name -> option type)
                                       representing the fingerprintable options
                                       and the scopes they are registered for.
  :param list passthru_args: A list of passthrough args (specified after `--` on the command line).
  :returns: A fake `Options` object with defaults populated for the given `optionables` and any
            explicitly set `options` overlayed.
  """
  all_options = defaultdict(dict)
  fingerprintable_options = defaultdict(dict)
  bootstrap_option_values = None

  if options_fingerprintable:
    for scope, opts in options_fingerprintable.items():
      fingerprintable_options[scope].update(opts)

  def register_func(on_scope):
    scoped_options = all_options[on_scope]
    scoped_fingerprintables = fingerprintable_options[on_scope]
    register = _options_registration_function(scoped_options, scoped_fingerprintables)
    register.bootstrap = bootstrap_option_values
    register.scope = on_scope
    return register

  # TODO: This sequence is a bit repetitive of the real registration sequence.

  # Register bootstrap options and grab their default values for use in subsequent registration.
  GlobalOptionsRegistrar.register_bootstrap_options(register_func(GLOBAL_SCOPE))
  bootstrap_option_values = _FakeOptionValues(all_options[GLOBAL_SCOPE].copy())

  # Now register the full global scope options.
  GlobalOptionsRegistrar.register_options(register_func(GLOBAL_SCOPE))

  for optionable in optionables:
    optionable.register_options(register_func(optionable.options_scope))

  if options:
    for scope, opts in options.items():
      all_options[scope].update(opts)

  return create_options(all_options,
                        passthru_args=passthru_args,
                        fingerprintable_options=fingerprintable_options)
Example #4
0
def create_options_for_optionables(optionables,
                                   extra_scopes=None,
                                   options=None):
    """Create a fake Options object for testing with appropriate defaults for the given optionables.

  Any scoped `options` provided will override defaults, behaving as-if set on the command line.

  :param iterable optionables: A series of `Optionable` types to register default options for.
  :param iterable extra_scopes: An optional series of extra known scopes in play.
  :param dict options: A dict of scope -> (dict of option name -> value) representing option values
                       explicitly set via the command line.
  :returns: A fake `Options` object with defaults populated for the given `optionables` and any
            explicitly set `options` overlayed.
  """
    all_options = defaultdict(dict)
    bootstrap_option_values = None

    def complete_scopes(scopes):
        """Return all enclosing scopes.

    This is similar to what `complete_scopes` does in `pants.option.options.Options` without
    creating `ScopeInfo`s.
    """
        completed_scopes = set(scopes)
        for scope in scopes:
            while scope != '':
                if scope not in completed_scopes:
                    completed_scopes.add(scope)
                scope = enclosing_scope(scope)
        return completed_scopes

    def register_func(on_scope):
        scoped_options = all_options[on_scope]
        register = _options_registration_function(scoped_options)
        register.bootstrap = bootstrap_option_values
        register.scope = on_scope
        return register

    # TODO: This sequence is a bit repetitive of the real registration sequence.

    # Register bootstrap options and grab their default values for use in subsequent registration.
    GlobalOptionsRegistrar.register_bootstrap_options(
        register_func(GLOBAL_SCOPE))
    bootstrap_option_values = create_option_values(
        all_options[GLOBAL_SCOPE].copy())

    # Now register the full global scope options.
    GlobalOptionsRegistrar.register_options(register_func(GLOBAL_SCOPE))

    for optionable in optionables:
        optionable.register_options(register_func(optionable.options_scope))

    # Make inner scopes inherit option values from their enclosing scopes.
    all_scopes = set(all_options.keys())

    # TODO(John Sirois): Kill extra scopes one this goes in:
    #   https://github.com/pantsbuild/pants/issues/1957
    # For now we need a way for users of this utility to provide extra derived scopes out of band.
    # With #1957 resolved, the extra scopes will be embedded in the Optionable's option_scope
    # directly.
    if extra_scopes:
        all_scopes.update(extra_scopes)

    all_scopes = complete_scopes(all_scopes)

    # Iterating in sorted order guarantees that we see outer scopes before inner scopes,
    # and therefore only have to inherit from our immediately enclosing scope.
    for s in sorted(all_scopes):
        if s != GLOBAL_SCOPE:
            scope = enclosing_scope(s)
            opts = all_options[s]
            for key, val in all_options.get(scope, {}).items():
                if key not in opts:  # Inner scope values override the inherited ones.
                    opts[key] = val

    if options:
        for scope, opts in options.items():
            all_options[scope].update(opts)
    return create_options(all_options)
Example #5
0
  def context(self, for_task_types=None, options=None, target_roots=None,
              console_outstream=None, workspace=None):
    for_task_types = for_task_types or []
    options = options or {}

    option_values = defaultdict(dict)
    registered_subsystems = set()
    bootstrap_option_values = None  # We fill these in after registering bootstrap options.

    # We provide our own test-only registration implementation, bypassing argparse.
    # When testing we set option values directly, so we don't care about cmd-line flags, config,
    # env vars etc. In fact, for test isolation we explicitly don't want to look at those.
    # All this does is make the names available in code, with the default values.
    # Individual tests can then override the option values they care about.
    def register_func(on_scope):
      def register(*rargs, **rkwargs):
        scoped_options = option_values[on_scope]
        default = rkwargs.get('default')
        if default is None and rkwargs.get('action') == 'append':
          default = []
        for flag_name in rargs:
          option_name = flag_name.lstrip('-').replace('-', '_')
          scoped_options[option_name] = default
      register.bootstrap = bootstrap_option_values
      register.scope = on_scope
      return register

    # TODO: This sequence is a bit repetitive of the real registration sequence.

    # Register bootstrap options and grab their default values for use in subsequent registration.
    GlobalOptionsRegistrar.register_bootstrap_options(register_func(Options.GLOBAL_SCOPE))
    bootstrap_option_values = create_option_values(copy.copy(option_values[Options.GLOBAL_SCOPE]))

    # Now register the full global scope options.
    GlobalOptionsRegistrar.register_options(register_func(Options.GLOBAL_SCOPE))

    # Now register task and subsystem options for relevant tasks.
    for task_type in for_task_types:
      scope = task_type.options_scope
      if scope is None:
        raise TaskError('You must set a scope on your task type before using it in tests.')
      task_type.register_options(register_func(scope))
      for subsystem in (set(task_type.global_subsystems()) |
                        set(task_type.task_subsystems()) |
                        self._build_configuration.subsystems()):
        if subsystem not in registered_subsystems:
          subsystem.register_options(register_func(subsystem.options_scope))
          registered_subsystems.add(subsystem)

    # Now default option values override with any caller-specified values.
    # TODO(benjy): Get rid of the options arg, and require tests to call set_options.

    for scope, opts in options.items():
      for key, val in opts.items():
        option_values[scope][key] = val

    for scope, opts in self.options.items():
      for key, val in opts.items():
        option_values[scope][key] = val

    # Make inner scopes inherit option values from their enclosing scopes.
    all_scopes = set(option_values.keys())
    for task_type in for_task_types:  # Make sure we know about pre-task subsystem scopes.
      all_scopes.update([si.scope for si in task_type.known_scope_infos()])
    # Iterating in sorted order guarantees that we see outer scopes before inner scopes,
    # and therefore only have to inherit from our immediately enclosing scope.
    for scope in sorted(all_scopes):
      if scope != Options.GLOBAL_SCOPE:
        enclosing_scope = scope.rpartition('.')[0]
        opts = option_values[scope]
        for key, val in option_values.get(enclosing_scope, {}).items():
          if key not in opts:  # Inner scope values override the inherited ones.
            opts[key] = val

    context = create_context(options=option_values,
                             target_roots=target_roots,
                             build_graph=self.build_graph,
                             build_file_parser=self.build_file_parser,
                             address_mapper=self.address_mapper,
                             console_outstream=console_outstream,
                             workspace=workspace)
    Subsystem._options = context.options
    return context
Example #6
0
def create_options_for_optionables(optionables,
                                   extra_scopes=None,
                                   options=None,
                                   options_fingerprintable=None,
                                   passthru_args=None):
  """Create a fake Options object for testing with appropriate defaults for the given optionables.

  Any scoped `options` provided will override defaults, behaving as-if set on the command line.

  :param iterable optionables: A series of `Optionable` types to register default options for.
  :param iterable extra_scopes: An optional series of extra known scopes in play.
  :param dict options: A dict of scope -> (dict of option name -> value) representing option values
                       explicitly set via the command line.
  :param dict options_fingerprintable: A dict of scope -> (dict of option name -> option type)
                                       representing the fingerprintable options
                                       and the scopes they are registered for.
  :param list passthru_args: A list of passthrough args (specified after `--` on the command line).
  :returns: A fake `Options` object with defaults populated for the given `optionables` and any
            explicitly set `options` overlayed.
  """
  all_options = defaultdict(dict)
  fingerprintable_options = defaultdict(dict)
  bootstrap_option_values = None

  # NB(cosmicexplorer): we do this again for all_options after calling
  # register_func below, this is a hack
  if options:
    for scope, opts in options.items():
      all_options[scope].update(opts)
  if options_fingerprintable:
    for scope, opts in options_fingerprintable.items():
      fingerprintable_options[scope].update(opts)

  def complete_scopes(scopes):
    """Return all enclosing scopes.

    This is similar to what `complete_scopes` does in `pants.option.options.Options` without
    creating `ScopeInfo`s.
    """
    completed_scopes = set(scopes)
    for scope in scopes:
      while scope != GLOBAL_SCOPE:
        if scope not in completed_scopes:
          completed_scopes.add(scope)
        scope = enclosing_scope(scope)
    return completed_scopes

  def register_func(on_scope):
    scoped_options = all_options[on_scope]
    scoped_fingerprintables = fingerprintable_options[on_scope]
    register = _options_registration_function(scoped_options, scoped_fingerprintables)
    register.bootstrap = bootstrap_option_values
    register.scope = on_scope
    return register

  # TODO: This sequence is a bit repetitive of the real registration sequence.

  # Register bootstrap options and grab their default values for use in subsequent registration.
  GlobalOptionsRegistrar.register_bootstrap_options(register_func(GLOBAL_SCOPE))
  bootstrap_option_values = _FakeOptionValues(all_options[GLOBAL_SCOPE].copy())

  # Now register the full global scope options.
  GlobalOptionsRegistrar.register_options(register_func(GLOBAL_SCOPE))

  for optionable in optionables:
    optionable.register_options(register_func(optionable.options_scope))

  # Make inner scopes inherit option values from their enclosing scopes.
  all_scopes = set(all_options.keys())

  # TODO(John Sirois): Kill extra scopes one this goes in:
  #   https://github.com/pantsbuild/pants/issues/1957
  # For now we need a way for users of this utility to provide extra derived scopes out of band.
  # With #1957 resolved, the extra scopes will be embedded in the Optionable's option_scope
  # directly.
  if extra_scopes:
    all_scopes.update(extra_scopes)

  all_scopes = complete_scopes(all_scopes)

  # We need to update options before completing them based on inner/outer relation.
  if options:
    for scope, opts in options.items():
      all_options[scope].update(opts)

  # Iterating in sorted order guarantees that we see outer scopes before inner scopes,
  # and therefore only have to inherit from our immediately enclosing scope.
  for s in sorted(all_scopes):
    if s != GLOBAL_SCOPE:
      scope = enclosing_scope(s)
      opts = all_options[s]
      for key, val in all_options.get(scope, {}).items():
        if key not in opts:  # Inner scope values override the inherited ones.
          opts[key] = val

  return create_options(all_options,
                        passthru_args=passthru_args,
                        fingerprintable_options=fingerprintable_options)
Example #7
0
    def context(self,
                for_task_types=None,
                options=None,
                target_roots=None,
                console_outstream=None,
                workspace=None):
        for_task_types = for_task_types or []
        options = options or {}

        option_values = defaultdict(dict)
        registered_subsystems = set()
        bootstrap_option_values = None  # We fill these in after registering bootstrap options.

        # We provide our own test-only registration implementation, bypassing argparse.
        # When testing we set option values directly, so we don't care about cmd-line flags, config,
        # env vars etc. In fact, for test isolation we explicitly don't want to look at those.
        # All this does is make the names available in code, with the default values.
        # Individual tests can then override the option values they care about.
        def register_func(on_scope):
            def register(*rargs, **rkwargs):
                scoped_options = option_values[on_scope]
                default = rkwargs.get('default')
                if default is None and rkwargs.get('action') == 'append':
                    default = []
                for flag_name in rargs:
                    option_name = flag_name.lstrip('-').replace('-', '_')
                    scoped_options[option_name] = default

            register.bootstrap = bootstrap_option_values
            register.scope = on_scope
            return register

        # TODO: This sequence is a bit repetitive of the real registration sequence.

        # Register bootstrap options and grab their default values for use in subsequent registration.
        GlobalOptionsRegistrar.register_bootstrap_options(
            register_func(Options.GLOBAL_SCOPE))
        bootstrap_option_values = create_option_values(
            copy.copy(option_values[Options.GLOBAL_SCOPE]))

        # Now register the full global scope options.
        GlobalOptionsRegistrar.register_options(
            register_func(Options.GLOBAL_SCOPE))

        # Now register task and subsystem options for relevant tasks.
        for task_type in for_task_types:
            scope = task_type.options_scope
            if scope is None:
                raise TaskError(
                    'You must set a scope on your task type before using it in tests.'
                )
            task_type.register_options(register_func(scope))
            for subsystem in (set(task_type.global_subsystems())
                              | set(task_type.task_subsystems())
                              | self._build_configuration.subsystems()):
                if subsystem not in registered_subsystems:
                    subsystem.register_options(
                        register_func(subsystem.options_scope))
                    registered_subsystems.add(subsystem)

        # Now default option values override with any caller-specified values.
        # TODO(benjy): Get rid of the options arg, and require tests to call set_options.

        for scope, opts in options.items():
            for key, val in opts.items():
                option_values[scope][key] = val

        for scope, opts in self.options.items():
            for key, val in opts.items():
                option_values[scope][key] = val

        # Make inner scopes inherit option values from their enclosing scopes.
        all_scopes = set(option_values.keys())
        for task_type in for_task_types:  # Make sure we know about pre-task subsystem scopes.
            all_scopes.update(
                [si.scope for si in task_type.known_scope_infos()])
        # Iterating in sorted order guarantees that we see outer scopes before inner scopes,
        # and therefore only have to inherit from our immediately enclosing scope.
        for scope in sorted(all_scopes):
            if scope != Options.GLOBAL_SCOPE:
                enclosing_scope = scope.rpartition('.')[0]
                opts = option_values[scope]
                for key, val in option_values.get(enclosing_scope, {}).items():
                    if key not in opts:  # Inner scope values override the inherited ones.
                        opts[key] = val

        context = create_context(options=option_values,
                                 target_roots=target_roots,
                                 build_graph=self.build_graph,
                                 build_file_parser=self.build_file_parser,
                                 address_mapper=self.address_mapper,
                                 console_outstream=console_outstream,
                                 workspace=workspace)
        Subsystem._options = context.options
        return context