from collections.abc import Container, Sequence, Mapping from typing import TypeAlias, TypeVar ParamsFilterSpec: TypeAlias = ( bool | Container[str] | tuple[bool, Container[str]] | Mapping[str, 'ParamsFilterSpec'] | tuple[bool, 'ParamsFilterSpec'] ) V = TypeVar('V') def _split_param_key(key: str) -> tuple[str, ...]: return tuple(key.split('__')) def _match_key_to_filter_spec( key: Sequence[str], spec: ParamsFilterSpec, empty_default: bool, ) -> bool: if isinstance(spec, Sequence) and (len(spec) == 2) and isinstance(spec[0], bool): if (len(key) == 0) and (not spec[0]): return empty_default spec = spec[1] if isinstance(spec, Mapping): if len(key) == 0: return empty_default spec_nested = spec.get(key[0]) if spec_nested is None: return False return _whether_to_include_param(key[1:], spec_nested) elif isinstance(spec, Container): if len(key) == 0: return True return (key[0] in spec) return bool(spec) def _whether_to_include_param( key: Sequence[str], include: ParamsFilterSpec = True, exclude: ParamsFilterSpec = False, ) -> bool: return ( (not _match_key_to_filter_spec(key, exclude, empty_default=False)) and _match_key_to_filter_spec(key, include, empty_default=True) ) def filter_params( params: Mapping[str, V], include: ParamsFilterSpec = True, exclude: ParamsFilterSpec = False, ) -> Mapping[str, V]: return { k: v for k, v in params.items() if _whether_to_include_param(_split_param_key(k), include, exclude) }