blob: 4a4c21839d3bc1cae5d294f7fa7f4cff71a5f1ee [file] [log] [blame]
"""
Define facilities for automatically applying rules to data.
"""
import os
import re
from typing import Callable, Dict, List
from lnt.util import logger
def load_rules():
"""
Load available rules scripts from a directory.
Rules are organized as:
<current dir>/rules/
<current dir>/rules/rule_.*.py
...
"""
rule_script_rex = re.compile(
r'^rule_(.*)\.py$')
rule_scripts = {}
rules_path = os.path.join(os.path.dirname(__file__), 'rules')
for item in os.listdir(rules_path):
# Ignore certain known non-scripts.
if item in ('README.txt', '__init__.py',
'__pycache__') or item.endswith('.pyc'):
continue
# Ignore non-matching files.
m = rule_script_rex.match(item)
if m is None:
logger.warning("ignoring item {} in rule directory: {}"
.format(item, rules_path))
continue
name = m.groups()[0]
# Allow rules to be disabled by name
if name.endswith("disabled"):
continue
rule_scripts[name] = os.path.join(rules_path, item)
return rule_scripts
# Places our rules can hook to.
HOOKS = {
'post_test_hook': [],
'post_submission_hook': [],
'post_regression_create_hook': [],
'is_useful_change': [],
} # type: Dict['str', List[Callable]]
DESCRIPTIONS = {}
HOOKS_LOADED = False
def register_hooks():
"""Exec all the rules files. Gather the hooks from them
and load them into the hook dict for later use.
"""
global HOOKS_LOADED
for name, path in load_rules().items():
globals = {}
with open(path) as f:
exec(compile(f.read(), path, 'exec'), globals)
DESCRIPTIONS[name] = globals['__doc__']
for hook_name in HOOKS.keys():
if hook_name in globals:
HOOKS[hook_name].append(globals[hook_name])
HOOKS_LOADED = True
return HOOKS
def post_submission_hooks(session, ts, run_id):
"""Run all the post submission hooks on the submitted run."""
if not HOOKS_LOADED:
logger.error("Running Hooks without loading them first.")
for func in HOOKS['post_submission_hook']:
func(session, ts, run_id)
def is_useful_change(session, ts, field_change):
"""Run all the change filters. If any are false, drop this change."""
all_filters = []
for func in HOOKS['is_useful_change']:
decision = func(session, ts, field_change)
all_filters.append(decision)
if len(all_filters) == 0:
return True
else:
# If any filter ignores, we ignore.
return all(all_filters)