from typing import Mapping
from typing import Any
from typing import Dict
import collections


def flatten_dict(input_dict: Mapping[str, Any],
                 separator: str) -> Dict[str, Any]:
  """Flattens mappings by joining sub-keys using the provided separator.

  Only non-empty, mapping types will be flattened. All other types are deemed
  leaf values.

  Args:
    input_dict: Mapping of key-value pairs to flatten.
    separator: Delimiter used to concatenate keys.

  Returns:
    Flattened dictionary of key-value pairs.

  Raises:
    ValueError: If the `input_dict` has a key that contains the separator
      string.
  """
  result = collections.OrderedDict()
  for key, value in input_dict.items():
    if separator in key:
      raise ValueError(f"Key '{key}' already contains separator!")
    if isinstance(value, Mapping) and len(value):
      result.update({
          f'{key}{separator}{sub_key}': sub_value
          for sub_key, sub_value in flatten_dict(value, separator).items()
      })
    else:
      result[key] = value
  return result