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.
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