[docs]def__init__(self,/,*,levels=DEFAULT_LEVELS):#: index: internal index to generate unique rule namesself._index=0#: levels: tuple of hierarchical level namesself._levels=levels
[docs]defcompile_scripts(self,source:list|str|Callable,/,*,level=0):# only one script? let's work on it as a (one item) list anyway.ifisinstance(source,str)orcallable(source):source=[source]forraw_sourceinsource:ifcallable(raw_source):yieldExecutableObject(raw_source)else:yieldScript(raw_source,filename=f"<rule:{self._index}>")self._index+=1
[docs]defcompile(self,source:dict,/,*,target:dict|None=None,level=0,_levels=None):""" Recursively compile a dictionary of rules. { pattern: ... } where "..." can be another dict or a sequence of scripts (str, list of str or other things we decide can be executed (maybe strings are only a shortcut for {type: script, src: ...}, we may want to have an extended dict base syntax later that can use custom implementations)). """#: target: target dict to store the compiled rules, allowing the parent to pass ana already partial ruleset,#: which is required for multi-files ruleset compilation.target=targetor{}# what level are we compiling for? what's remaining?if_levelsisNone:_levels=self._levelsifnotlen(_levels):raiseValueError("No more rule level available.")current_level,*remaining_levels=_levels# iterate over the source dict, a mapping between patterns and either more sources (recursive) or scripts if# we are at the last level.forkey,valueinsource.items():_pattern=self.compile_pattern(current_level,key)# are we at the last level? then we should compile scriptsifnotremaining_levels:ifisinstance(value,(list,str))orcallable(value):if_patternnotintarget:target[_pattern]=[]target[_pattern].extend(self.compile_scripts(value,level=level+1))else:raiseValueError("Last level of rules dict must be a list of scripts or a single script.")# otherwise, we should compile the next levelelifisinstance(value,dict):if_patternnotintarget:target[_pattern]={}target[_pattern]=self.compile(value,target=target[_pattern],level=level+1,_levels=remaining_levels,)else:raiseValueError(f"Invalid value type for key {key}: {type(value)}")returntarget