mozilla_schema_generator/matcher.py (58 lines of code) (raw):
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
from typing import Any
from .probes import Probe
class Matcher(object):
table_group_key = "table_group"
type_key = "type"
contains_key = "contains"
not_key = "not"
any_key = "any"
keywords = {contains_key, not_key, type_key, table_group_key, any_key}
def __init__(self, match_obj: dict, *, _type=None, table_group=None):
"""
table_group: required, which table that group belongs in
type: optional, the type of the metrics
All other fields are matched as exact matches,
except for `contains` which checks that that value
is in the associated array.
"""
self.table_group = table_group or match_obj.get(self.table_group_key)
self.type = _type or match_obj.get(self.type_key)
self.matcher = {
k: v
for k, v in match_obj.items()
if k not in {self.table_group_key, self.type_key}
}
def get_table_group(self):
return self.table_group
def matches(self, probe: Probe) -> bool:
# Not a match if the types don't match
if self.type and self.type != probe.get_type():
return False
for k, v in self.matcher.items():
probe_value = probe.get(k)
if not self._matches(v, probe_value):
return False
# Definitions are nested, check sub-fields (e.g. details)
if isinstance(v, dict):
for sub_k, sub_v in v.items():
if sub_k not in self.keywords and not self._matches(
sub_v, probe_value[sub_k]
):
return False
return True
def clone(self, new_type=None, new_table_group=None):
if new_table_group is None:
new_table_group = self.table_group
if new_type is None:
new_type = self.type
return Matcher(self.matcher, _type=new_type, table_group=new_table_group)
@staticmethod
def _matches(match_v: Any, probe_v: Any) -> bool:
if isinstance(probe_v, set):
probe_v = list(probe_v)
# Not a match if this key isn't in the probe definition
if probe_v is None:
return False
# Not a match if not an exact match of values (e.g. type=scalar vs. histogram)
if not isinstance(match_v, dict):
if match_v != probe_v:
return False
elif isinstance(match_v, dict):
# Not a match if probe_v doesn't contain expected value
if Matcher.contains_key in match_v:
if match_v[Matcher.contains_key] not in probe_v:
return False
# Not a match if matches the "not" value
if Matcher.not_key in match_v:
if match_v[Matcher.not_key] == probe_v:
return False
# Match if any of the listed values match
if Matcher.any_key in match_v:
return probe_v in match_v[Matcher.any_key]
return True