commit: 862594b151befc3ac3e1766df370890c8af88470 Author: André Erdmann <dywi <AT> mailerd <DOT> de> AuthorDate: Thu Jul 17 16:26:32 2014 +0000 Commit: André Erdmann <dywi <AT> mailerd <DOT> de> CommitDate: Thu Jul 17 16:26:32 2014 +0000 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=862594b1
add-policy rule-gen: implement token generatorion --- .../packagerules/generators/addition_control.py | 883 +++++++-------------- 1 file changed, 279 insertions(+), 604 deletions(-) diff --git a/roverlay/packagerules/generators/addition_control.py b/roverlay/packagerules/generators/addition_control.py index 73040e0..7f2cc95 100644 --- a/roverlay/packagerules/generators/addition_control.py +++ b/roverlay/packagerules/generators/addition_control.py @@ -1,684 +1,342 @@ -# R overlay -- package rule generators, addition control +# R overlay -- abstract package rule generators, addition control # -*- coding: utf-8 -*- # Copyright (C) 2014 André Erdmann <d...@mailerd.de> # Distributed under the terms of the GNU General Public License; # either version 2 of the License, or (at your option) any later version. from __future__ import absolute_import -# temporary_demo_func(): -from __future__ import print_function import abc -# __metaclass__/metaclass= workaround -_AbstractObject = abc.ABCMeta ( str("AbstractObject"), ( object, ), {} ) - import collections +import fnmatch +import re + + +import roverlay.packagerules.generators.abstract.addition_control -#~ import roverlay.util.fileio -import roverlay.util.namespace -#~ import roverlay.packagerules.abstract.acceptors -import roverlay.packagerules.abstract.rules -#~ import roverlay.packagerules.acceptors.stringmatch -#~ import roverlay.packagerules.acceptors.util -import roverlay.packagerules.acceptors.trivial -import roverlay.packagerules.actions.addition_control -#~ -#~ from roverlay.packagerules.abstract.rules import ( - #~ NestedPackageRule, PackageRule -#~ ) -#~ -#~ from roverlay.packagerules.acceptors.stringmatch ( - #~ NocaseStringAcceptor, StringAcceptor, - #~ RegexAcceptor, ExactRegexAcceptor -#~ ) -#~ -#~ from roverlay.packagerules.acceptors.util import ( - #~ get_package, get_package_name, get_ebuild_name, get_category, - #~ DEFAULT_CATEGORY_REPLACEMENT -#~ ) - -import roverlay.overlay.abccontrol -from roverlay.overlay.abccontrol import AdditionControlResult - - -# converting addition-control lists (cmdline, from file...) to rule objects, -# hacky solution: -# -# ** step 1 ** -- collect category/package tokens, determine bitmask -# -# create a dict ( -# category_token|True => package_token|True => bitmask<policy> -# ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ ^~~~~~~~~~~~~~^ -# "acceptor chain" "add-policy" -# ) -# -# "True" means "accept all". -# -# make sure that the tokens get de-duped -# (normalize values, use tuples (or namespace)) -# -# Only the first and the last step need to know *what* category/package -# tokens are. All intermediate steps should not need to care about this. -# - - - -class AbstractAdditionControlPackageRuleGenerator ( _AbstractObject ): - """(Abstract) object that takes cmdline/files as input and creates - add-policy package rules.""" - - # Note that tokens are not totally abstract, - # the "match-all" (True) token is hardcoded - - #CategoryToken = collections.namedtuple ( 'CategoryToken', '<attr>*' ) - #PackageToken = collections.namedtuple ( 'PackageToken', '<attr>*' ) - - @abc.abstractmethod - def category_token_to_acceptor ( self, category_token, priority ): - """Creates a package rule acceptor for the given category token. +import roverlay.packagerules.acceptors.stringmatch +import roverlay.packagerules.acceptors.util - Returns: not-None acceptor (or nested acceptor) - Must not return None. - If a token is meaningless, then don't create it in the first place. +from roverlay.packagerules.abstract.acceptors import Acceptor_AND - arguments: - * category_token -- a category token - * priority -- priority of the acceptor (int) - """ - raise NotImplementedError() - # --- end of category_token_to_acceptor (...) --- +from roverlay.packagerules.acceptors.stringmatch import ( + StringAcceptor, ExactRegexAcceptor, +) - @abc.abstractmethod - def package_token_to_acceptor ( self, package_token, priority ): - """Creates a package rule acceptor for the given package token. +from roverlay.packagerules.acceptors.util import ( + get_category, get_ebuild_name, get_ebuild_version_tuple, +) - Returns: not-None acceptor (or nested acceptor) +#import roverlay.util.fileio +import roverlay.util.namespace - arguments: - * package_token -- a package token - * priority -- priority of the acceptor (int) - """ - raise NotImplementedError() - # --- end of package_token_to_acceptor (...) --- +import roverlay.util.portage_regex.default +import roverlay.util.portage_regex.wildcard +from roverlay.util.portage_regex.default import RE_PACKAGE_EBUILD_FILE +from roverlay.util.portage_regex.wildcard import ( + RE_WILDCARD_PACKAGE, RE_WILDCARD_CATEGORY +) - def create_package_rules ( self, reduced_bitmask_acceptor_chain_map ): - """Creates a nested add-policy package rule object. - The rule object's priority has to be set manually afterwards. - - Returns: (nested) package rule or None - """ - # create_package_rules() is defined/implemented below (step 5) - return create_package_rules ( - reduced_bitmask_acceptor_chain_map, - convert_category_token_to_acceptor = self.category_token_to_acceptor, - convert_package_token_to_acceptor = self.package_token_to_acceptor - ) - # --- end of create_package_rules (...) --- - def prepare_bitmask_map ( self, acceptor_chain_bitmask_map ): - """Transforms the given "acceptor chain" -> "bitmask" map into the - reduced "effective bitmask" -> "acceptor chain" map. - Note: Involves in-place operations that modify - acceptor_chain_bitmask_map. - Pass a copy if the original map should remain unchanged. - Returns: reduced/optimized "effective bitmask" -> "acceptor chain" - arguments: - * acceptor_chain_bitmask_map -- "acceptor chain" -> "bitmask" map - """ - expand_acceptor_chain_bitmasks ( acceptor_chain_bitmask_map ) - bitmask_acceptor_chain_map = ( - create_bitmask_acceptor_chain_map ( acceptor_chain_bitmask_map ) - ) - reduce_bitmask_acceptor_chain_map ( bitmask_acceptor_chain_map ) - return bitmask_acceptor_chain_map - # --- end of prepare_bitmask_map (...) --- - def compile_bitmask_map ( self, acceptor_chain_bitmask_map ): - """Transforms the given "acceptor chain" -> "bitmask" map into a - (nested) package rule. - This is equal to calling - obj.create_package_rules ( - obj.prepare_bitmask_map ( acceptor_chain_bitmask_map ) - ) +class TokenValueError ( ValueError ): + pass - Returns: (nested) rule object or None +class InvalidTokenInputString ( TokenValueError ): + pass - arguments: - * acceptor_chain_bitmask_map -- "acceptor chain" -> "bitmask" map - """ - return self.create_package_rules ( - prepare_bitmask_map ( acceptor_chain_bitmask_map ) - ) - # --- end of compile_bitmask_map (...) --- - - -# --- end of AbstractAdditionControlPackageRuleGenerator --- - - -# -# ** step 2 ** -- expand global and category-wide bitmasks, -# set effective package bitmask -# (FIXME: does it make sense to apply the global bitmask?) -# -# for each category with at least one non-True package_token loop -# for each package in category loop -# category->package |= category->True [bitwise-OR] -# end loop -# (do not modify category->True, as it applies to packages not matched -# by any package_token, too) -# end loop -# -# for each category loop -# for each entry in category loop -# reduce policy bitmask (keep effective bits only) -# end loop -# end loop -# -# (merge the two for-loops in code) -# - -def expand_acceptor_chain_bitmasks ( acceptor_chain_bitmask_map ): - """Expands a "acceptor chain" -> "bitmask" map. - (Sets the effective bitmask, propagates global bitmasks, ...) - - In-place operation that modifies the acceptor_chain_bitmask_map arg. - - Returns: None (implicit) - - arguments: - * acceptor_chain_bitmask_map -- "acceptor chain" -> "bitmask" map - """ - # naming convention: >e<ffective bit>mask< => emask - - get_emask = AdditionControlResult.get_effective_package_policy - - def normalize_entry ( mapping, key, additional_emask=0 ): - """Determines and sets the effective bitmask of mapping->key. - - Returns: None (implicit) - - arguments: - * mapping -- dict-like object - * key -- dict key (has to exist) - * additional_emask -- effective bitmask that should be propagated to - mapping->key (global/category-wide bitmask) - """ - new_value = get_emask ( mapping [key] | additional_emask ) - mapping [key] = new_value - return new_value - # --- end of normalize_entry (...) --- - - def normalize_entry_maybe_missing ( mapping, key, additional_emask=0 ): - """Like normalize_entry(), but does not require the existence of - mapping->key (the entry will be created if necessary). - - arguments: - * mapping -- - * key -- - * additional_emask -- - """ - if key in mapping: - return normalize_entry ( mapping, key, additional_emask ) - else: - mapping [key] = additional_emask - return additional_emask - # --- end of normalize_entry_maybe_missing (...) --- +class TokenItemNotSupported ( TokenValueError ): + pass - # propagate global/category-wide emask to package_token entries - # acceptor_chain_bitmask_map->True->True is the global emask - if True in acceptor_chain_bitmask_map: - global_emask = normalize_entry_maybe_missing ( - acceptor_chain_bitmask_map [True], True - ) - else: - global_emask = 0 - for category_token, package_token_map in acceptor_chain_bitmask_map.items(): - # --- cannot modify acceptor_chain_bitmask_map in this block - category_emask = normalize_entry_maybe_missing ( - package_token_map, True, global_emask - ) +class AdditionControlPackageRuleGenerator ( + roverlay.packagerules.generators.abstract.addition_control.\ + AbstractAdditionControlPackageRuleGenerator +): - for package_token in package_token_map: - # --- cannot modify acceptor_chain_bitmask_map, package_token_map - if package_token is not True: - # else already processed (category_emask) - normalize_entry ( - package_token_map, package_token, category_emask - ) - # -- end if <package_token> - # -- end for <package_token> - - # -- end for <category_token,package_token_map> - -# --- end of expand_acceptor_chain_bitmasks (...) --- - - -# -# ** step 3 -- create reversed map ** -# -# BITMASK_MAP: create a dict ( -# effective_bitmask => category_token|True => set(package_token|True>) -# ^~~~~~~~~~~~~~~~^ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^ -# "add-policy atom" "acceptor chain" -# ) -# -# -# **hacky**: -# It would be better to split effective_bitmask into its components -# (2**k for k in ...), but this requires path compaction. -# Not implemented. (graph/spanning-tree and/or logic minimization) -# - -def create_bitmask_acceptor_chain_map ( acceptor_chain_bitmask_map ): - """Transforms a "acceptor chain" -> "[effective] bitmask" map - into a "[effective] bitmask" -> "acceptor chain" mask without applying - any optimization/reduction steps. - - Returns: "[effective] bitmask" -> "acceptor chain" map - - arguments: - * acceptor_chain_bitmask_map -- "acceptor chain" -> "bitmask" map - Should be in expanded form - (expand_acceptor_chain_bitmasks()) - """ - bitmask_acceptor_chain_map = {} + #TOKEN_ITEM_IS_SPECIAL = 0 ## implicit + TOKEN_ITEM_IS_STR = 1 + TOKEN_ITEM_IS_REGEX = 2 - for category_token, package_token_map in acceptor_chain_bitmask_map.items(): + TokenItemTuple = collections.namedtuple ( + "TokenItemTuple", "item_type value" + ) - for package_token, emask in package_token_map.items(): - try: - emask_entry = bitmask_acceptor_chain_map [emask] - except KeyError: - bitmask_acceptor_chain_map [emask] = { - category_token: set ({ package_token, }) - } - else: - try: - category_entry = emask_entry [category_token] - except KeyError: - emask_entry [category_token] = set ({ package_token, }) - else: - category_entry.add ( package_token ) - # -- end for <package token map> - - # -- end for <category token map> - - return bitmask_acceptor_chain_map -# --- end of create_bitmask_acceptor_chain_map (...) --- - - -# -# ** step 4 -- naive path compaction ** -# (Keep in mind that acceptor tokens cannot be merged with match-all) -# -# reduce acceptor chains (drop-superseeded): -# for each effective_bitmask b in BITMASK_MAP loop -# if BITMASK_MAP->b->True->True exists then -# drop all other entries from BITMASK_MAP->b -# else -# for each category in BITMASK_MAP->b loop -# if category->True exists then -# drop all other entries from category -# end if -# end loop -# end if -# end loop -# -# (optional: drop overlapping acceptor chains, e.g. regex ".*" is equal -# to True and therefore all other entries in the regex' branch can be -# removed) -# -# -# merge-bitmask: (OPTIONAL / NOT IMPLEMENTED) -# (bitwise-OR effective_bitmasks with identical acceptor chains) -# -# *could* create a large table -# <add-policy atom X category/package> => bool -# (where category, package can also be "accept-all") -# -# -# +-----------------+-------+-------+-----+-------+-------+-----+-------+ -# | add-policy atom | c0/p0 | c0/p1 | ... | c0/pM | c1/p0 | ... | cN/pJ | -# +=================+=======+=======+=====+=======+=======+=====+=======+ -# | 2**0 | 0|1 | 0|1 | ... | 0|1 | 0|1 | ... | 0|1 | -# +-----------------+-------+-------+-----+-------+-------+-----+-------+ -# | ... | ... | ... | ... | ... | ... | ... | ... | -# +-----------------+-------+-------+-----+-------+-------+-----+-------+ -# | 2**k | 0|1 | 0|1 | ... | 0|1 | 0|1 | ... | 0|1 | -# +-----------------+-------+-------+-----+-------+-------+-----+-------+ -# -# ++ reduce table -# - -def reduce_bitmask_acceptor_chain_map ( bitmask_acceptor_chain_map ): - """Reduces/Optimizes a "effective bitmask" -> "acceptor chain" map. - - In-place operation, the bitmask_acceptor_chain_map will be modified. - - Returns: None (implicit) - - arguments: - * bitmask_acceptor_chain_map -- "effective bitmask" -> "acceptor chain" map - - Implementation detail: - The reduced map uses empty sets/dicts for representing "match-all" - acceptors. - """ + # PackageToken: tuple ( None|$PN, None|$PV, None|$PR ) + # (revision not implemented because it cannot be matched in package rules + # -- always 0) + # condition: not all ( item is None for item in <PackageToken> ) + # and all not-None items must evaluate to True + # <=> any ( <PackageToken> ) + # + PackageToken = collections.namedtuple ( 'PackageToken', 'name version' ) - # could be integrated in create_bitmask_acceptor_chain_map(), - # but kept separate for better readability + # CategoryToken: tuple ( $CATEGORY ) + # same conditions as for PackageToken apply, + # which effectively means that $CATEGORY must a non-empty str # + CategoryToken = collections.namedtuple ( 'CategoryToken', 'name' ) - # emask==0 can be ignored - bitmask_acceptor_chain_map.pop ( 0, True ) - - for emask in bitmask_acceptor_chain_map: - emask_matches_all = False - - for category_token, package_token_set in ( - bitmask_acceptor_chain_map [emask].items() - ): - if True not in package_token_set: - # category~DONT_CARE, package!=True <=> keep entries - pass - elif category_token is True: - # category==True, package==True <=> match all, *** BREAK LOOP *** - emask_matches_all = True - break - else: - # category!=True, package==True <=> match entire category - package_token_set.clear() - # -- end for <category->package token map> - - if emask_matches_all: - bitmask_acceptor_chain_map [emask].clear() - # -- end for <emask> -# --- end of reduce_bitmask_acceptor_chain_map (...) --- - - -# -# ** step 5 -- convert the acceptor chains to objects ** -# -# the final BITMASK_MAP can be easily translated into rule objects: -# -# * convert the the effective bitmask into one or more -# PackageAdditionControl*Actions -# -# * an OR<category OR<packages>> match statement -# represents the acceptor chain -# (reduce properly, e.g. "accept-all" entries) -# -# -# ** done ** -# - -def create_packagerule_action_map(): - """Helper function that creates all add-policy package rule actions. - - Returns: dict ( "bitmask atom" (2**k) -> "package rule action" ) - - arguments: None - """ - return { - action_cls.CONTROL_RESULT: action_cls ( - priority=action_cls.CONTROL_RESULT - ) - for action_cls in ( - getattr ( - roverlay.packagerules.actions.addition_control, - action_cls_name - ) for action_cls_name in ( - roverlay.packagerules.actions.addition_control.ACTIONS - ) - ) if action_cls.CONTROL_RESULT - } -# --- end of create_packagerule_action_map (...) --- + def create_token_item_from_str ( self, s ): + get_obj = self.namespace.get_object_v -def create_package_rules ( - reduced_bitmask_acceptor_chain_map, - convert_category_token_to_acceptor, - convert_package_token_to_acceptor -): - """Converts the given "effective bitmask" -> "acceptor chain" map - into a nested package rule. - - Returns: (nested) package rule or None - - arguments: - * reduced_bitmask_acceptor_chain_map -- reduced/optimized - "bitmask" -> "acceptor chain" map - * convert_category_token_to_acceptor -- function(token,priority) - -> category acceptor - * convert_package_token_to_acceptor -- function(token,priority) - -> package acceptor - """ - packagerule_actions = create_packagerule_action_map() - # true acceptor with priority -1 - always_true_acceptor = ( - roverlay.packagerules.acceptors.trivial.TrueAcceptor ( priority=-1 ) - ) + if not s: + return None + elif any ( char in "?*" for char in s ): + s_stripped = s.strip("*") + if not s_stripped or ( + s_stripped == "?" and s_stripped != s + ): + # "****", "?*", "*?", ... => match all + return True - def get_acceptor_recursive ( category_token_map, priority ): - """ - Creates a (possibly nested) acceptor for the given category_token_map. - - Returns: not-None acceptor - - arguments: - * category_token_map -- "category token" -> "package token" map - ("acceptor chain") - * priority -- "recommended" priority of the acceptor - Ignored when returning an always-true acceptor. - """ - - # Note: it's illegal to set an acceptor's priority after its creation - # this would violate namespace->get_object() - # ==> use 0 as priority for objects where it doesn't matter much - # (at the cost of unstable-sorted output) - # - # The acceptor gets wrapped with an Acceptor_AND object anyway(*), - # no need to set any priority. - # (*) "the top-level logic of a MATCH-block is AND by convention" - # the acceptor returned by get_*acceptor*() can be of *any* - # type, but its str representation (--print-package-rules) - # would always be the same. - # - # ["proper" prio-setting is already implemented at - # package/category acceptor level, so the outmost AND acceptor - # has exactly one member and the member's priority matches - # the acceptor's] - # - - def get_package_acceptor ( package_tokens, prio ): - """Creates a (nested) acceptor for the given non-empty iterable - of package tokens. - - Returns: not-None acceptor - - arguments: - * package_tokens -- iterable of package tokens (not empty) - * prio -- advised priority of the top-most acceptor - (the object being returned) - """ - if not package_tokens: - raise ValueError ( "package token set must not be empty!" ) - - elif len(package_tokens) == 1: - # special case: bind prio to package_acceptor - return convert_package_token_to_acceptor ( - next(iter(package_tokens)), prio + else: + return get_obj ( + self.__class__.TokenItemTuple, + ( + self.__class__.TOKEN_ITEM_IS_REGEX, + get_obj ( fnmatch.translate, ( s, ) ) + ) ) - else: - package_acceptors = [ - convert_package_token_to_acceptor ( package_token, 0 ) - for package_token in package_tokens - ] + else: + return get_obj ( + self.__class__.TokenItemTuple, + ( self.__class__.TOKEN_ITEM_IS_STR, s ) + ) + # --- end of create_token_item_from_str (...) --- + + def validate_token ( self, package_token ): + all_none = True + any_nonspecial = False + + for item in package_token: + if item: + all_none = False + if item is not True: + any_nonspecial = True + # -- end for + + if all_none: + assert not any_nonspecial + return None + elif any_nonspecial: + return package_token + else: + # FIXME: remove assert + assert all ( item in ( True, None ) for item in package_token ), package_token + return True + # --- end of validate_token (...) --- + + def _create_package_token ( self, name_str, version_str, revision_str ): + if revision_str: + raise TokenItemNotSupported ( + "revision {!s}".format ( revision_str ) + ) - # package acceptors get ORed, no need to set a specific priority - combined_acceptor = ( - roverlay.packagerules.abstract.acceptors.Acceptor_OR ( prio ) - ) + package_token = self.namespace.get_object_v ( + self.__class__.PackageToken, + ( + self.create_token_item_from_str ( name_str ), + self.create_token_item_from_str ( version_str ), +## self.create_token_item_from_str ( revision_str ), + ) + ) - for package_acceptor in package_acceptors: - combined_acceptor.add_acceptor ( package_acceptor ) + return self.validate_token ( package_token ) + # --- end of _create_package_token (...) --- - return combined_acceptor + def _create_category_token ( self, category_str ): + category_token = self.namespace.get_object_v ( + self.__class__.CategoryToken, + ( + self.create_token_item_from_str ( category_str ), + ) + ) - # --- end of get_package_acceptor (...) --- + return self.validate_token ( category_token ) + # --- end of _create_category_token (...) --- + + def create_category_token ( self, input_str ): + # input_str doesn't need much parsing, + # not using RE_WILDCARD_CATEGORY for now +## re_match = RE_WILDCARD_CATEGORY.match ( input_str ) +## if not re_match: +## raise InvalidTokenInputString ( input_str ) +## +## match_vars = re_match.groupdict() +## +## if not match_vars ['CATEGORY']: +## raise InvalidTokenInputString ( input_str ) + + if not input_str: + raise InvalidTokenInputString ( input_str ) + else: + return self._create_category_token ( input_str ) + # --- end of create_category_token (...) --- - def get_category_acceptor ( category_token, package_tokens, prio ): - """Creates a nested acceptor for the given category token and its - package tokens. - Returns: not-None acceptor + def create_token ( + self, input_str, with_category=True, package_regex=RE_WILDCARD_PACKAGE + ): + re_match = package_regex.match ( input_str ) - arguments: - * category_token -- category token (True or non-empty) - * package_tokens -- iterable of package tokens (can be empty) - """ + if not re_match: + raise InvalidTokenInputString ( + "{!s}: not matched by regex".format ( input_str ) + ) - if category_token is True: - return get_package_acceptor ( package_tokens, prio ) + match_vars = re_match.groupdict() - elif package_tokens: - acceptor = ( - roverlay.packagerules.abstract.acceptors.Acceptor_AND ( prio ) - ) - # match category first and then packages - acceptor.add_acceptor ( - convert_category_token_to_acceptor ( category_token, 0 ) - ) - acceptor.add_acceptor ( - get_package_acceptor ( package_tokens, 1 ) - ) + package_token = self._create_package_token ( + match_vars ['PN'], match_vars ['PV'], match_vars ['revision'] + ) - return acceptor + if not package_token: + raise InvalidTokenInputString ( input_str ) - else: - return convert_category_token_to_acceptor ( category_token, prio ) - # --- end of get_category_acceptor (...) --- - - if not category_token_map: - # match-all - return always_true_acceptor - - elif len(category_token_map) == 1: - # special case: bind priority to entry - category_acceptor = None - for category_token, package_tokens in category_token_map.items(): - if category_acceptor is None: - category_acceptor = get_category_acceptor ( - category_token, package_tokens, priority - ) - else: - raise AssertionError ( "must not loop more than once." ) - # -- end for - return category_acceptor + if with_category: + category_token = self._create_category_token ( + match_vars ['CATEGORY'] + ) - else: - acceptors = [ - get_category_acceptor ( category_token, package_tokens, k ) - for ( k, ( category_token, package_tokens ) ) in enumerate ( - category_token_map.items() + if not category_token: + if match_vars ['CATEGORY']: + raise InvalidTokenInputString ( + "{!s}: invalid category str {!s}".format ( + input_str, match_vars ['CATEGORY'] + ) ) - ] + # -- - combined_acceptor = ( - roverlay.packagerules.abstract.acceptors.Acceptor_OR ( priority ) + # => match-all + category_token = True + # -- end if + + return ( category_token, package_token ) + + elif match_vars ['CATEGORY']: + raise InvalidTokenInputString ( + "{!s}: must not contain CATEGORY".format ( input_str ) ) + else: + return package_token + # --- end of create_token (...) --- - for acceptor in acceptors: - combined_acceptor.add_acceptor ( acceptor ) + def create_token_for_ebuild_filepath ( self, efile ): + return self.create_token ( + efile, with_category=True, package_regex=RE_PACKAGE_EBUILD_FILE + ) + # --- end of create_token_for_ebuild_filepath (...) --- - return combined_acceptor + def create_package_token ( self, input_str ): + return self.create_token ( input_str, with_category=False ) + # --- end of create_package_token (...) --- - # --- end of get_acceptor_recursive (...) --- - def create_rule ( category_token_map, emask ): - """Creates a package rule for the given acceptor chain - ("category token" -> "package tokens" map) + def __init__ ( self, default_category ): + super ( AdditionControlPackageRuleGenerator, self ).__init__() + self.namespace = roverlay.util.namespace.SimpleNamespace() - Returns: not-None not-nested package rule (with a nested match block) + def clear_object_cache ( self ): + if self.namespace: + self.namespace.zap(True) + # --- end of clear_object_cache (...) --- - arguments: - * category_token_map -- acceptor chain - * emask -- effective bitmask that gets translated into - its corresponding package rule actions - """ + def __del__ ( self ): + try: + self.clear_object_cache() + except NotImplementedError: + pass - # wrap actual acceptor with Acceptor_AND, see above - actual_acceptor = get_acceptor_recursive ( category_token_map, 0 ) - and_acceptor = roverlay.packagerules.abstract.acceptors.Acceptor_AND (0) - and_acceptor.add_acceptor ( actual_acceptor ) + def token_item_to_acceptor ( self, token_item, value_getter, priority ): + if token_item in ( True, None ): + # should be catched elsewhere + return None - rule = roverlay.packagerules.abstract.rules.PackageRule ( priority=emask ) + elif token_item.item_type == self.__class__.TOKEN_ITEM_IS_STR: + acceptor_cls = StringAcceptor - rule.set_acceptor ( and_acceptor ) - have_any_action = False - for k, action in packagerule_actions.items(): - have_any_action = True - if ( k & emask ): - rule.add_action ( action ) + elif token_item.item_type == self.__class__.TOKEN_ITEM_IS_REGEX: + acceptor_cls = ExactRegexAcceptor - if not have_any_action: + else: raise AssertionError ( - "rule object has no actions - bad emask? {:#x}".format ( - emask=emask - ) + "invalid token item type: {!s}".format ( token_item.item_type ) ) + # -- end if - return rule - # --- end of create_rule (...) --- - - rules = [ - create_rule ( category_token_map, emask ) - for emask, category_token_map in \ - reduced_bitmask_acceptor_chain_map.items() - ] + return self.namespace.get_object_v ( + acceptor_cls, ( priority, value_getter, token_item.value ) + ) + # --- end of token_item_to_acceptor (...) --- - if not rules: - return None + def category_token_to_acceptor ( self, category_token, priority ): + if category_token is True: + raise Exception ( "C-TRUE" ) - elif len(rules) == 1: - rules [0].priority = -1 - return rules [0] - else: - combined_rule = roverlay.packagerules.abstract.rules.NestedPackageRule ( - priority = -1 + return self.token_item_to_acceptor ( + category_token.name, get_category, priority ) - combined_rule.set_acceptor ( always_true_acceptor ) + # --- end of category_token_to_acceptor (...) --- - for rule in rules: - combined_rule.add_rule ( rule ) + def package_token_to_acceptor ( self, package_token, priority ): + if package_token is True: + raise Exception ( "P-TRUE" ) + else: + relevant_items = [ + item_and_getter for item_and_getter in zip ( + package_token, + ( + get_ebuild_name, + get_ebuild_version_tuple + ) + ) if item_and_getter[0] and item_and_getter[0] is not True + ] - return combined_rule - # -- end if + if not relevant_items: + raise TokenValueError ( package_token ) + + elif len(relevant_items) == 1: + return self.token_item_to_acceptor ( + relevant_items[0][0], relevant_items[0][1], priority + ) + else: + sub_acceptors = [ + self.token_item_to_acceptor ( item, getter, 0 ) + for item, getter in relevant_items + ] + + combined_acceptor = Acceptor_AND ( priority=priority ) + for sub_acceptor in sub_acceptors: + combined_acceptor.add_acceptor ( sub_acceptor ) + + return combined_acceptor + # --- end of package_token_to_acceptor (...) --- -# --- end of create_package_rules (...) --- class SillyAdditionControlPackageRuleGenerator ( - AbstractAdditionControlPackageRuleGenerator + roverlay.packagerules.generators.abstract.addition_control.\ + AbstractAdditionControlPackageRuleGenerator ): """ An add-policy package rule generator that doesn't care about its tokens. @@ -705,41 +363,53 @@ class SillyAdditionControlPackageRuleGenerator ( def temporary_demo_func(): - rule_generator = SillyAdditionControlPackageRuleGenerator() + #rule_generator = SillyAdditionControlPackageRuleGenerator() + ARES = AdditionControlResult + rule_generator = AdditionControlPackageRuleGenerator("sci-R") + CTOKEN = rule_generator.create_category_token + PTOKEN = rule_generator.create_package_token add_control_rule = None bitmask_acceptor_chain_map = None acceptor_chain_bitmask_map = { - True: { - True: AdditionControlResult.PKG_REVBUMP_ON_COLLISION, - 'p0': 8|2, + CTOKEN ( "sys-*" ): { + PTOKEN ( "a*-2" ): ARES.PKG_FORCE_DENY, + }, + CTOKEN ( "*" ): { + PTOKEN ( "?*" ): ARES.PKG_REVBUMP_ON_COLLISION, + PTOKEN ( 'p0' ): ARES.PKG_FORCE_DENY|ARES.PKG_FORCE_REPLACE, }, - 'c': { - True: 2, + CTOKEN ( 'c' ): { + PTOKEN ( "***?****" ): ARES.PKG_FORCE_DENY, }, - 'd': { - True: AdditionControlResult.PKG_REPLACE_ONLY, + CTOKEN ( 'd' ): { + PTOKEN ( "*" ): ARES.PKG_REPLACE_ONLY, }, - 'f': { - 'p1': AdditionControlResult.PKG_REPLACE_ONLY, + CTOKEN ( 'f' ): { + PTOKEN ( 'p1' ): ARES.PKG_REPLACE_ONLY, + PTOKEN ( 'p1-5.0' ): ARES.PKG_REPLACE_ONLY, }, } + print ( "** initial acceptor_chain -> raw_bitmask map" ) print ( acceptor_chain_bitmask_map ) print() + roverlay.packagerules.generators.abstract.addition_control.\ expand_acceptor_chain_bitmasks ( acceptor_chain_bitmask_map ) print ( "** expanded acceptor_chain -> effective_bitmask map" ) print ( acceptor_chain_bitmask_map ) print() bitmask_acceptor_chain_map = ( + roverlay.packagerules.generators.abstract.addition_control.\ create_bitmask_acceptor_chain_map ( acceptor_chain_bitmask_map ) ) print ( "** initial effective_bitmask -> acceptor_chain map" ) print(bitmask_acceptor_chain_map) print() + roverlay.packagerules.generators.abstract.addition_control.\ reduce_bitmask_acceptor_chain_map ( bitmask_acceptor_chain_map ) print ( "** reduced effective_bitmask -> acceptor_chain map" ) print(bitmask_acceptor_chain_map) @@ -758,12 +428,17 @@ def temporary_demo_func(): print ( "** content of the rule generator\'s namespace" ) print ( rule_generator.namespace._objects ) + print ( "** content of the rule generator\'s namespace after clear-cache" ) + rule_generator.clear_object_cache() + print ( rule_generator.namespace._objects ) + # --- end of temporary_demo_func (...) --- if __name__ == '__main__': import sys import os + from roverlay.overlay.abccontrol import AdditionControlResult try: temporary_demo_func()