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

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.

table_group_key = 'table_group'
type_key = 'type'
contains_key = 'contains'
not_key = 'not'
any_key = 'any'
keywords = {'type', 'contains', 'table_group', 'any', 'not'}
table_group
type
matcher
def get_table_group(self):
40    def get_table_group(self):
41        return self.table_group
def matches(self, probe: mozilla_schema_generator.probes.Probe) -> bool:
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
def clone(self, new_type=None, new_table_group=None):
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)