mozilla_schema_generator.matcher

  1# -*- coding: utf-8 -*-
  2
  3# This Source Code Form is subject to the terms of the Mozilla Public
  4# License, v. 2.0. If a copy of the MPL was not distributed with this
  5# file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7from typing import Any
  8
  9from .probes import Probe
 10
 11
 12class Matcher(object):
 13
 14    table_group_key = "table_group"
 15    type_key = "type"
 16    contains_key = "contains"
 17    not_key = "not"
 18    any_key = "any"
 19
 20    keywords = {contains_key, not_key, type_key, table_group_key, any_key}
 21
 22    def __init__(self, match_obj: dict, *, _type=None, table_group=None):
 23        """
 24        table_group: required, which table that group belongs in
 25        type: optional, the type of the metrics
 26
 27        All other fields are matched as exact matches,
 28        except for `contains` which checks that that value
 29        is in the associated array.
 30        """
 31        self.table_group = table_group or match_obj.get(self.table_group_key)
 32        self.type = _type or match_obj.get(self.type_key)
 33
 34        self.matcher = {
 35            k: v
 36            for k, v in match_obj.items()
 37            if k not in {self.table_group_key, self.type_key}
 38        }
 39
 40    def get_table_group(self):
 41        return self.table_group
 42
 43    def matches(self, probe: Probe) -> bool:
 44        # Not a match if the types don't match
 45        if self.type and self.type != probe.get_type():
 46            return False
 47
 48        for k, v in self.matcher.items():
 49            probe_value = probe.get(k)
 50
 51            if not self._matches(v, probe_value):
 52                return False
 53
 54            # Definitions are nested, check sub-fields (e.g. details)
 55            if isinstance(v, dict):
 56                for sub_k, sub_v in v.items():
 57                    if sub_k not in self.keywords and not self._matches(
 58                        sub_v, probe_value[sub_k]
 59                    ):
 60                        return False
 61
 62        return True
 63
 64    def clone(self, new_type=None, new_table_group=None):
 65        if new_table_group is None:
 66            new_table_group = self.table_group
 67        if new_type is None:
 68            new_type = self.type
 69
 70        return Matcher(self.matcher, _type=new_type, table_group=new_table_group)
 71
 72    @staticmethod
 73    def _matches(match_v: Any, probe_v: Any) -> bool:
 74        if isinstance(probe_v, set):
 75            probe_v = list(probe_v)
 76
 77        # Not a match if this key isn't in the probe definition
 78        if probe_v is None:
 79            return False
 80
 81        # Not a match if not an exact match of values (e.g. type=scalar vs. histogram)
 82        if not isinstance(match_v, dict):
 83            if match_v != probe_v:
 84                return False
 85
 86        elif isinstance(match_v, dict):
 87            # Not a match if probe_v doesn't contain expected value
 88            if Matcher.contains_key in match_v:
 89                if match_v[Matcher.contains_key] not in probe_v:
 90                    return False
 91
 92            # Not a match if matches the "not" value
 93            if Matcher.not_key in match_v:
 94                if match_v[Matcher.not_key] == probe_v:
 95                    return False
 96
 97            # Match if any of the listed values match
 98            if Matcher.any_key in match_v:
 99                return probe_v in match_v[Matcher.any_key]
100        return True
class Matcher:
 13class Matcher(object):
 14
 15    table_group_key = "table_group"
 16    type_key = "type"
 17    contains_key = "contains"
 18    not_key = "not"
 19    any_key = "any"
 20
 21    keywords = {contains_key, not_key, type_key, table_group_key, any_key}
 22
 23    def __init__(self, match_obj: dict, *, _type=None, table_group=None):
 24        """
 25        table_group: required, which table that group belongs in
 26        type: optional, the type of the metrics
 27
 28        All other fields are matched as exact matches,
 29        except for `contains` which checks that that value
 30        is in the associated array.
 31        """
 32        self.table_group = table_group or match_obj.get(self.table_group_key)
 33        self.type = _type or match_obj.get(self.type_key)
 34
 35        self.matcher = {
 36            k: v
 37            for k, v in match_obj.items()
 38            if k not in {self.table_group_key, self.type_key}
 39        }
 40
 41    def get_table_group(self):
 42        return self.table_group
 43
 44    def matches(self, probe: Probe) -> bool:
 45        # Not a match if the types don't match
 46        if self.type and self.type != probe.get_type():
 47            return False
 48
 49        for k, v in self.matcher.items():
 50            probe_value = probe.get(k)
 51
 52            if not self._matches(v, probe_value):
 53                return False
 54
 55            # Definitions are nested, check sub-fields (e.g. details)
 56            if isinstance(v, dict):
 57                for sub_k, sub_v in v.items():
 58                    if sub_k not in self.keywords and not self._matches(
 59                        sub_v, probe_value[sub_k]
 60                    ):
 61                        return False
 62
 63        return True
 64
 65    def clone(self, new_type=None, new_table_group=None):
 66        if new_table_group is None:
 67            new_table_group = self.table_group
 68        if new_type is None:
 69            new_type = self.type
 70
 71        return Matcher(self.matcher, _type=new_type, table_group=new_table_group)
 72
 73    @staticmethod
 74    def _matches(match_v: Any, probe_v: Any) -> bool:
 75        if isinstance(probe_v, set):
 76            probe_v = list(probe_v)
 77
 78        # Not a match if this key isn't in the probe definition
 79        if probe_v is None:
 80            return False
 81
 82        # Not a match if not an exact match of values (e.g. type=scalar vs. histogram)
 83        if not isinstance(match_v, dict):
 84            if match_v != probe_v:
 85                return False
 86
 87        elif isinstance(match_v, dict):
 88            # Not a match if probe_v doesn't contain expected value
 89            if Matcher.contains_key in match_v:
 90                if match_v[Matcher.contains_key] not in probe_v:
 91                    return False
 92
 93            # Not a match if matches the "not" value
 94            if Matcher.not_key in match_v:
 95                if match_v[Matcher.not_key] == probe_v:
 96                    return False
 97
 98            # Match if any of the listed values match
 99            if Matcher.any_key in match_v:
100                return probe_v in match_v[Matcher.any_key]
101        return True
Matcher(match_obj: dict, *, _type=None, table_group=None)
23    def __init__(self, match_obj: dict, *, _type=None, table_group=None):
24        """
25        table_group: required, which table that group belongs in
26        type: optional, the type of the metrics
27
28        All other fields are matched as exact matches,
29        except for `contains` which checks that that value
30        is in the associated array.
31        """
32        self.table_group = table_group or match_obj.get(self.table_group_key)
33        self.type = _type or match_obj.get(self.type_key)
34
35        self.matcher = {
36            k: v
37            for k, v in match_obj.items()
38            if k not in {self.table_group_key, self.type_key}
39        }

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.

def get_table_group(self):
41    def get_table_group(self):
42        return self.table_group
def matches(self, probe: mozilla_schema_generator.probes.Probe) -> bool:
44    def matches(self, probe: Probe) -> bool:
45        # Not a match if the types don't match
46        if self.type and self.type != probe.get_type():
47            return False
48
49        for k, v in self.matcher.items():
50            probe_value = probe.get(k)
51
52            if not self._matches(v, probe_value):
53                return False
54
55            # Definitions are nested, check sub-fields (e.g. details)
56            if isinstance(v, dict):
57                for sub_k, sub_v in v.items():
58                    if sub_k not in self.keywords and not self._matches(
59                        sub_v, probe_value[sub_k]
60                    ):
61                        return False
62
63        return True
def clone(self, new_type=None, new_table_group=None):
65    def clone(self, new_type=None, new_table_group=None):
66        if new_table_group is None:
67            new_table_group = self.table_group
68        if new_type is None:
69            new_type = self.type
70
71        return Matcher(self.matcher, _type=new_type, table_group=new_table_group)