Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add callback to adjust() to make it observable by callers #197

Merged
merged 1 commit into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions fmf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import subprocess
from io import open
from pprint import pformat as pretty
from typing import Any, Dict, Optional, Protocol

import jsonschema
from ruamel.yaml import YAML
Expand All @@ -29,6 +30,25 @@
# Metadata
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


class AdjustCallback(Protocol):
"""
A callback for per-rule notifications made by Tree.adjust()

Function be be called for every rule inspected by adjust().
It will be given three arguments: fmf tree being inspected,
current adjust rule, and whether the rule was skipped (``None``),
applied (``True``) or not applied (``False``).
"""

def __call__(
self,
node: 'Tree',
rule: Dict[str, Any],
applied: Optional[bool]) -> None:
pass


class Tree:
""" Metadata Tree """

Expand Down Expand Up @@ -320,7 +340,7 @@ def update(self, data):
log.debug("Data for '{0}' updated.".format(self))
log.data(pretty(self.data))

def adjust(self, context, key='adjust', undecided='skip'):
def adjust(self, context, key='adjust', undecided='skip', decision_callback=None):
"""
Adjust tree data based on provided context and rules

Expand All @@ -334,6 +354,10 @@ class describing the environment context. By default, the key
context dimension is not defined. By default, such rules are
skipped. In order to raise the fmf.context.CannotDecide
exception in such cases use undecided='raise'.

Optional 'decision_callback' callback would be called for every adjust
rule inspected, with three arguments: current fmf node, current
adjust rule, and whether it was applied or not.
"""

# Check context sanity
Expand Down Expand Up @@ -363,6 +387,8 @@ class describing the environment context. By default, the key
if not isinstance(rule, dict):
raise utils.FormatError("Adjust rule should be a dictionary.")

original_rule = rule.copy()

# Missing 'when' means always enabled rule
try:
condition = rule.pop('when')
Expand All @@ -382,13 +408,22 @@ class describing the environment context. By default, the key
# Apply remaining rule attributes if context matches
try:
if context.matches(condition):
if decision_callback:
decision_callback(self, original_rule, True)

self._merge_special(self.data, rule)

# First matching rule wins, skip the rest unless continue
if not continue_:
break
else:
if decision_callback:
decision_callback(self, original_rule, False)
# Handle undecided rules as requested
except fmf.context.CannotDecide:
if decision_callback:
decision_callback(self, original_rule, None)

if undecided == 'skip':
continue
elif undecided == 'raise':
Expand All @@ -400,7 +435,7 @@ class describing the environment context. By default, the key

# Adjust all child nodes as well
for child in self.children.values():
child.adjust(context, key, undecided)
child.adjust(context, key, undecided, decision_callback=decision_callback)

def get(self, name=None, default=None):
"""
Expand Down
17 changes: 17 additions & 0 deletions tests/unit/test_adjust.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import copy
from unittest.mock import MagicMock

import pytest
from ruamel.yaml import YAML
Expand Down Expand Up @@ -204,3 +205,19 @@ def test_continue_default(self, full, fedora):
full.adjust(fedora)
extend = full.find('/extend')
assert extend.get('require') == ['one', 'two', 'three']

def test_adjust_callback(self, mini, fedora, centos):
# From the mini tree
rule = mini.data['adjust']

mock_callback = MagicMock(name='<mock>callback')
mini.adjust(fmf.Context(), decision_callback=mock_callback)
mock_callback.assert_called_once_with(mini, rule, None)

mock_callback = MagicMock(name='<mock>callback')
mini.adjust(fedora, decision_callback=mock_callback)
mock_callback.assert_called_once_with(mini, rule, False)

mock_callback = MagicMock(name='<mock>callback')
mini.adjust(centos, decision_callback=mock_callback)
mock_callback.assert_called_once_with(mini, rule, True)
Loading