"""Merging of policies.""" from typing import ( # noqa: F401 cast, Dict, List, Set) from .types import PolicyType, CategoryType def merge_policies(policies: List[PolicyType]) -> PolicyType: """Merge policies.""" new_policy = {} # type: Dict[str, CategoryType] seen = set() # type: Set[str] for policy in policies: for category in policy: if category in seen: continue seen.add(category) new_policy[category] = _merge_policies([ policy.get(category) for policy in policies]) cast(PolicyType, new_policy) return new_policy def _merge_policies(sources: List[CategoryType]) -> CategoryType: """Merge a policy.""" # When merging policies, the most permissive wins. # This means we order it like this: # True > Dict > None # # True: allow everything # Dict: specify more granular permissions # None: no opinion # # If there are multiple sources with a dict as policy, we recursively # merge each key in the source. policy = None # type: CategoryType seen = set() # type: Set[str] for source in sources: if source is None: continue # A source that's True will always win. Shortcut return. if source is True: return True assert isinstance(source, dict) if policy is None: policy = cast(CategoryType, {}) assert isinstance(policy, dict) for key in source: if key in seen: continue seen.add(key) key_sources = [] for src in sources: if isinstance(src, dict): key_sources.append(src.get(key)) policy[key] = _merge_policies(key_sources) return policy