25
25
TIME_RANGE_PATTERN = re .compile (r"2[0-3]:[0-5]\d|[0-1]\d:[0-5]\d" ) # 24 hour clock
26
26
HOUR_MIN_SEPARATOR = ":"
27
27
28
+ LOGGER : logging .Logger | Logger = logging .getLogger (__name__ )
29
+
28
30
29
31
class RuleAction (str , Enum ):
30
32
EQUALS = "EQUALS"
@@ -196,7 +198,12 @@ class SchemaValidator(BaseValidator):
196
198
197
199
def __init__ (self , schema : Dict [str , Any ], logger : Optional [Union [logging .Logger , Logger ]] = None ):
198
200
self .schema = schema
199
- self .logger = logger or logging .getLogger (__name__ )
201
+ self .logger = logger or LOGGER
202
+
203
+ # Validators are designed for modular testing
204
+ # therefore we link the custom logger with global LOGGER
205
+ # so custom validators can use them when necessary
206
+ SchemaValidator ._link_global_logger (self .logger )
200
207
201
208
def validate (self ) -> None :
202
209
self .logger .debug ("Validating schema" )
@@ -206,13 +213,18 @@ def validate(self) -> None:
206
213
features = FeaturesValidator (schema = self .schema , logger = self .logger )
207
214
features .validate ()
208
215
216
+ @staticmethod
217
+ def _link_global_logger (logger : logging .Logger | Logger ):
218
+ global LOGGER
219
+ LOGGER = logger
220
+
209
221
210
222
class FeaturesValidator (BaseValidator ):
211
223
"""Validates each feature and calls RulesValidator to validate its rules"""
212
224
213
225
def __init__ (self , schema : Dict , logger : Optional [Union [logging .Logger , Logger ]] = None ):
214
226
self .schema = schema
215
- self .logger = logger or logging . getLogger ( __name__ )
227
+ self .logger = logger or LOGGER
216
228
217
229
def validate (self ):
218
230
for name , feature in self .schema .items ():
@@ -250,7 +262,7 @@ def __init__(
250
262
self .feature = feature
251
263
self .feature_name = next (iter (self .feature ))
252
264
self .rules : Optional [Dict ] = self .feature .get (RULES_KEY )
253
- self .logger = logger or logging . getLogger ( __name__ )
265
+ self .logger = logger or LOGGER
254
266
self .boolean_feature = boolean_feature
255
267
256
268
def validate (self ):
@@ -297,7 +309,7 @@ class ConditionsValidator(BaseValidator):
297
309
def __init__ (self , rule : Dict [str , Any ], rule_name : str , logger : Optional [Union [logging .Logger , Logger ]] = None ):
298
310
self .conditions : List [Dict [str , Any ]] = rule .get (CONDITIONS_KEY , {})
299
311
self .rule_name = rule_name
300
- self .logger = logger or logging . getLogger ( __name__ )
312
+ self .logger = logger or LOGGER
301
313
302
314
def validate (self ):
303
315
if not self .conditions or not isinstance (self .conditions , list ):
@@ -341,14 +353,17 @@ def validate_condition_key(condition: Dict[str, Any], rule_name: str):
341
353
# SCHEDULE_BETWEEN_DAYS_OF_WEEK_KEY
342
354
# - extra validation: `_validate_schedule_between_days_of_week_key`
343
355
#
344
- # maintenance: we can split to separate file and classes for better organization later, e.g., visitor pattern.
356
+ # maintenance: we should split to separate file/classes for better organization, e.g., visitor pattern.
357
+
358
+ custom_validator = getattr (ConditionsValidator , f"_validate_{ action .lower ()} _key" )
345
359
346
- custom_validator = getattr (
347
- ConditionsValidator ,
348
- f"_validate_ { action . lower () } _key" ,
349
- ConditionsValidator . _validate_noop_value ,
350
- )
360
+ # ~90% of actions available don't require a custom validator
361
+ # logging a debug statement for no-match will increase CPU cycles for most customers
362
+ # for that reason only, we invert and log only when extra validation is found.
363
+ if custom_validator is None :
364
+ return
351
365
366
+ LOGGER .debug (f"{ action } requires key validation. Running '{ custom_validator } ' validator." )
352
367
custom_validator (key , rule_name )
353
368
354
369
@staticmethod
@@ -364,19 +379,19 @@ def validate_condition_value(condition: Dict[str, Any], rule_name: str):
364
379
# SCHEDULE_BETWEEN_DAYS_OF_WEEK_KEY
365
380
# - extra validation: `_validate_schedule_between_days_of_week_value`
366
381
#
367
- # maintenance: we can split to separate file and classes for better organization later , e.g., visitor pattern.
382
+ # maintenance: we should split to separate file/ classes for better organization, e.g., visitor pattern.
368
383
369
- custom_validator = getattr (
370
- ConditionsValidator ,
371
- f"_validate_{ action .lower ()} _value" ,
372
- ConditionsValidator ._validate_noop_value ,
373
- )
384
+ custom_validator = getattr (ConditionsValidator , f"_validate_{ action .lower ()} _value" )
374
385
375
- custom_validator (value , rule_name )
386
+ # ~90% of actions available don't require a custom validator
387
+ # logging a debug statement for no-match will increase CPU cycles for most customers
388
+ # for that reason only, we invert and log only when extra validation is found.
389
+ if custom_validator is None :
390
+ return
376
391
377
- @ staticmethod
378
- def _validate_noop_value ( * args , ** kwargs ):
379
- return True
392
+ LOGGER . debug ( f" { action } requires value validation. Running ' { custom_validator } ' validator." )
393
+
394
+ custom_validator ( value , rule_name )
380
395
381
396
@staticmethod
382
397
def _validate_schedule_between_days_of_week_key (key : str , rule_name : str ):
0 commit comments