Beispiel #1
0
  def wrapper(func):
    if not inspect.isfunction(func):
      raise ValueError('The @rule decorator must be applied innermost of all decorators.')

    owning_module = sys.modules[func.__module__]
    source = inspect.getsource(func)
    beginning_indent = _get_starting_indent(source)
    if beginning_indent:
      source = "\n".join(line[beginning_indent:] for line in source.split("\n"))
    module_ast = ast.parse(source)

    def resolve_type(name):
      resolved = getattr(owning_module, name, None) or owning_module.__builtins__.get(name, None)
      if resolved is None:
        raise ValueError(
          f'Could not resolve type `{name}` in top level of module {owning_module.__name__}'
        )
      elif not isinstance(resolved, type):
        raise ValueError(
          f'Expected a `type` constructor for `{name}`, but got: {resolved} (type '
          f'`{type(resolved).__name__}`)'
        )
      return resolved

    gets = OrderedSet()
    rule_func_node = assert_single_element(
      node for node in ast.iter_child_nodes(module_ast)
      if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == func.__name__)

    parents_table = {}
    for parent in ast.walk(rule_func_node):
      for child in ast.iter_child_nodes(parent):
        parents_table[child] = parent

    rule_visitor = _RuleVisitor()
    rule_visitor.visit(rule_func_node)
    gets.update(
      Get.create_statically_for_rule_graph(resolve_type(p), resolve_type(s))
      for p, s in rule_visitor.gets)

    # Register dependencies for @goal_rule/Goal.
    dependency_rules = (subsystem_rule(return_type.subsystem_cls),) if is_goal_cls else None

    # Set a default name for Goal classes if one is not explicitly provided
    if is_goal_cls and name is None:
      effective_name = return_type.name
    else:
      effective_name = name

    func.rule = TaskRule(
        return_type,
        tuple(parameter_types),
        func,
        input_gets=tuple(gets),
        dependency_rules=dependency_rules,
        cacheable=cacheable,
        name=effective_name,
      )

    return func
Beispiel #2
0
    def signature(cls):
        """Returns kwargs to construct a `TaskRule` that will construct the target Optionable.

        TODO: This indirection avoids a cycle between this module and the `rules` module.
        """
        partial_construct_optionable = functools.partial(
            _construct_optionable, cls)

        # NB: We must populate several dunder methods on the partial function because partial functions
        # do not have these defined by default and the engine uses these values to visualize functions
        # in error messages and the rule graph.
        snake_scope = cls.options_scope.replace("-", "_")
        partial_construct_optionable.__name__ = f"construct_scope_{snake_scope}"
        partial_construct_optionable.__module__ = cls.__module__
        _, class_definition_lineno = inspect.getsourcelines(cls)
        partial_construct_optionable.__line_number__ = class_definition_lineno

        return dict(
            output_type=cls.optionable_cls,
            input_selectors=tuple(),
            func=partial_construct_optionable,
            input_gets=(Get.create_statically_for_rule_graph(
                ScopedOptions, Scope), ),
            dependency_optionables=(cls.optionable_cls, ),
        )
Beispiel #3
0
    def test_create(self):
        # Test the equivalence of the 2-arg and 3-arg versions.
        self.assertEqual(Get(AClass, BClass()), Get(AClass, BClass, BClass()))

        with self.assertRaises(TypeError) as cm:
            Get(AClass, BClass)
        self.assertEqual(
            """\
The two-argument form of Get does not accept a type as its second argument.

args were: Get(({a!r}, {b!r}))

Get.create_statically_for_rule_graph() should be used to generate a Get() for
the `input_gets` field of a rule. If you are using a `await Get(...)` in a rule
and a type was intended, use the 3-argument version:
Get({a!r}, {t!r}, {b!r})
""".format(a=AClass, t=type(BClass), b=BClass),
            str(cm.exception),
        )

        with self.assertRaises(ValueError) as cm:
            Get(1)
        self.assertEqual(
            "Expected either two or three arguments to Get; got (1,).",
            str(cm.exception))
Beispiel #4
0
  def wrapper(func):
    if not inspect.isfunction(func):
      raise ValueError('The @rule decorator must be applied innermost of all decorators.')

    owning_module = sys.modules[func.__module__]
    source = inspect.getsource(func)
    beginning_indent = _get_starting_indent(source)
    if beginning_indent:
      source = "\n".join(line[beginning_indent:] for line in source.split("\n"))
    module_ast = ast.parse(source)

    def resolve_type(name):
      resolved = getattr(owning_module, name, None) or owning_module.__builtins__.get(name, None)
      if resolved is None:
        raise ValueError('Could not resolve type `{}` in top level of module {}'
                         .format(name, owning_module.__name__))
      elif not isinstance(resolved, type):
        raise ValueError('Expected a `type` constructor for `{}`, but got: {} (type `{}`)'
                         .format(name, resolved, type(resolved).__name__))
      return resolved

    gets = OrderedSet()
    rule_func_node = assert_single_element(
      node for node in ast.iter_child_nodes(module_ast)
      if isinstance(node, ast.FunctionDef) and node.name == func.__name__)

    parents_table = {}
    for parent in ast.walk(rule_func_node):
      for child in ast.iter_child_nodes(parent):
        parents_table[child] = parent

    rule_visitor = _RuleVisitor(
      func=func,
      func_node=rule_func_node,
      func_source=source,
      orig_indent=beginning_indent,
      parents_table=parents_table,
    )
    rule_visitor.visit(rule_func_node)
    gets.update(
      Get.create_statically_for_rule_graph(resolve_type(p), resolve_type(s))
      for p, s in rule_visitor.gets)

    # Register dependencies for @console_rule/Goal.
    if is_goal_cls:
      dependency_rules = (optionable_rule(output_type.Options),)
    else:
      dependency_rules = None

    func.rule = TaskRule(
        output_type,
        tuple(input_selectors),
        func,
        input_gets=tuple(gets),
        dependency_rules=dependency_rules,
        cacheable=cacheable,
      )

    return func
Beispiel #5
0
  def signature(cls):
    """Returns kwargs to construct a `TaskRule` that will construct the target Optionable.

    TODO: This indirection avoids a cycle between this module and the `rules` module.
    """
    snake_scope = cls.options_scope.replace('-', '_')
    partial_construct_optionable = functools.partial(_construct_optionable, cls)
    partial_construct_optionable.__name__ = 'construct_scope_{}'.format(snake_scope)
    return dict(
        output_type=cls.optionable_cls,
        input_selectors=tuple(),
        func=partial_construct_optionable,
        input_gets=(Get.create_statically_for_rule_graph(ScopedOptions, Scope),),
        dependency_optionables=(cls.optionable_cls,),
      )
Beispiel #6
0
    def signature(cls):
        """Returns kwargs to construct a `TaskRule` that will construct the target Optionable.

    TODO: This indirection avoids a cycle between this module and the `rules` module.
    """
        snake_scope = cls.options_scope.replace('-', '_')
        partial_construct_optionable = functools.partial(
            _construct_optionable, cls)
        partial_construct_optionable.__name__ = f'construct_scope_{snake_scope}'
        return dict(
            output_type=cls.optionable_cls,
            input_selectors=tuple(),
            func=partial_construct_optionable,
            input_gets=(Get.create_statically_for_rule_graph(
                ScopedOptions, Scope), ),
            dependency_optionables=(cls.optionable_cls, ),
        )
Beispiel #7
0
  def test_create(self):
    # Test the equivalence of the 2-arg and 3-arg versions.
    self.assertEqual(Get(AClass, BClass()),
                     Get(AClass, BClass, BClass()))

    with self.assertRaises(TypeError) as cm:
      Get(AClass, BClass)
    self.assertEqual("""\
The two-argument form of Get does not accept a type as its second argument.

args were: Get(({a!r}, {b!r}))

Get.create_statically_for_rule_graph() should be used to generate a Get() for
the `input_gets` field of a rule. If you are using a `yield Get(...)` in a rule
and a type was intended, use the 3-argument version:
Get({a!r}, {t!r}, {b!r})
""".format(a=AClass, t=type(BClass), b=BClass), str(cm.exception))

    with self.assertRaises(ValueError) as cm:
      Get(1)
    self.assertEqual("Expected either two or three arguments to Get; got (1,).",
                     str(cm.exception))
Beispiel #8
0
 def test_create_statically_for_rule_graph(self):
     self.assertEqual(Get(AClass, BClass, None),
                      Get.create_statically_for_rule_graph(AClass, BClass))
Beispiel #9
0
 def test_create_statically_for_rule_graph(self):
   self.assertEqual(Get(AClass, BClass, None),
                    Get.create_statically_for_rule_graph(AClass, BClass))
Beispiel #10
0
    def wrapper(func):
        if not inspect.isfunction(func):
            raise ValueError(
                "The @rule decorator must be applied innermost of all decorators."
            )

        owning_module = sys.modules[func.__module__]
        source = inspect.getsource(func)
        beginning_indent = _get_starting_indent(source)
        if beginning_indent:
            source = "\n".join(line[beginning_indent:]
                               for line in source.split("\n"))
        module_ast = ast.parse(source)

        def resolve_type(name):
            resolved = getattr(owning_module, name,
                               None) or owning_module.__builtins__.get(
                                   name, None)
            if resolved is None:
                raise ValueError(
                    f"Could not resolve type `{name}` in top level of module {owning_module.__name__}"
                )
            elif not isinstance(resolved, type):
                raise ValueError(
                    f"Expected a `type` constructor for `{name}`, but got: {resolved} (type "
                    f"`{type(resolved).__name__}`)")
            return resolved

        rule_func_node = assert_single_element(
            node for node in ast.iter_child_nodes(module_ast)
            if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
            and node.name == func.__name__)

        parents_table = {}
        for parent in ast.walk(rule_func_node):
            for child in ast.iter_child_nodes(parent):
                parents_table[child] = parent

        rule_visitor = _RuleVisitor()
        rule_visitor.visit(rule_func_node)

        gets = FrozenOrderedSet(
            Get.create_statically_for_rule_graph(resolve_type(p),
                                                 resolve_type(s))
            for p, s in rule_visitor.gets)

        # Register dependencies for @goal_rule/Goal.
        dependency_rules = (subsystem_rule(
            return_type.subsystem_cls), ) if is_goal_cls else None

        # Set a default canonical name if one is not explicitly provided. For Goal classes
        # this is the name of the Goal; for other named ruled this is the __name__ of the function
        # that implements it.
        effective_name = annotations.canonical_name
        if effective_name is None:
            effective_name = return_type.name if is_goal_cls else func.__name__
        normalized_annotations = RuleAnnotations(canonical_name=effective_name,
                                                 desc=annotations.desc)

        # Set our own custom `__line_number__` dunder so that the engine may visualize the line number.
        func.__line_number__ = func.__code__.co_firstlineno

        func.rule = TaskRule(
            return_type,
            tuple(parameter_types),
            func,
            input_gets=tuple(gets),
            dependency_rules=dependency_rules,
            cacheable=cacheable,
            annotations=normalized_annotations,
        )

        return func
Beispiel #11
0
    def wrapper(func):
        if not inspect.isfunction(func):
            raise ValueError(
                'The @rule decorator must be applied innermost of all decorators.'
            )

        owning_module = sys.modules[func.__module__]
        source = inspect.getsource(func)
        beginning_indent = _get_starting_indent(source)
        if beginning_indent:
            source = "\n".join(line[beginning_indent:]
                               for line in source.split("\n"))
        module_ast = ast.parse(source)

        def resolve_type(name):
            resolved = getattr(owning_module, name,
                               None) or owning_module.__builtins__.get(
                                   name, None)
            if resolved is None:
                raise ValueError(
                    'Could not resolve type `{}` in top level of module {}'.
                    format(name, owning_module.__name__))
            elif not isinstance(resolved, type):
                raise ValueError(
                    'Expected a `type` constructor for `{}`, but got: {} (type `{}`)'
                    .format(name, resolved,
                            type(resolved).__name__))
            return resolved

        gets = OrderedSet()
        rule_func_node = assert_single_element(
            node for node in ast.iter_child_nodes(module_ast) if
            isinstance(node, ast.FunctionDef) and node.name == func.__name__)

        parents_table = {}
        for parent in ast.walk(rule_func_node):
            for child in ast.iter_child_nodes(parent):
                parents_table[child] = parent

        rule_visitor = _RuleVisitor(
            func=func,
            func_node=rule_func_node,
            func_source=source,
            orig_indent=beginning_indent,
            parents_table=parents_table,
        )
        rule_visitor.visit(rule_func_node)
        gets.update(
            Get.create_statically_for_rule_graph(resolve_type(p),
                                                 resolve_type(s))
            for p, s in rule_visitor.gets)

        # For @console_rule, redefine the function to avoid needing a literal return of the output type.
        if for_goal:

            def goal_and_return(*args, **kwargs):
                res = func(*args, **kwargs)
                if isinstance(res, GeneratorType):
                    # Return a generator with an output_type instance appended.
                    return _terminated(res, output_type())
                elif res is not None:
                    raise Exception(
                        'A @console_rule should not have a return value.')
                return output_type()

            functools.update_wrapper(goal_and_return, func)
            wrapped_func = goal_and_return
        else:
            wrapped_func = func

        wrapped_func.rule = TaskRule(
            output_type,
            tuple(input_selectors),
            wrapped_func,
            input_gets=tuple(gets),
            goal=for_goal,
            cacheable=cacheable,
        )

        return wrapped_func