docs: def filter(filter, data, sensitive=True, regexp=False):
      """
      Return true if provided filter matches given dictionary of values
  
      Filter supports disjunctive normal form with '|' used for OR, '&'
      for AND and '-' for negation. Individual values are prefixed with
      'value:', leading/trailing white-space is stripped. For example::
  
          tag: Tier1 | tag: Tier2 | tag: Tier3
          category: Sanity, Security & tag: -destructive
  
      Note that multiple comma-separated values can be used as a syntactic
      sugar to shorten the filter notation::
  
          tag: A, B, C ---> tag: A | tag: B | tag: C
  
      Values should be provided as a dictionary of lists each describing
      the values against which the filter is to be matched. For example::
  
          data = {tag: ["Tier1", "TIPpass"], category: ["Sanity"]}
  
      Other types of dictionary values are converted into a string.
      A FilterError exception is raised when a dimension parsed from the
      filter is not found in the data dictionary. Set option 'sensitive'
      to False to enable case-insensitive matching. If 'regexp' option is
      True, regular expressions can be used in the filter values as well.
      """
  
      def match_value(pattern, text):
          """ Match value against data (simple or regexp) """
          if regexp:
              return re.match("^{0}$".format(pattern), text)
          else:
              return pattern == text
  
      def check_value(dimension, value):
          """ Check whether the value matches data """
          # E.g. value = 'A, B' or value = "C" or value = "-D"
          # If there are multiple values, at least one must match
          for atom in re.split("\s*,\s*", value):
              # Handle negative values (check the whole data for non-presence)
              if atom.startswith("-"):
                  atom = atom[1:]
                  # Check each value for given dimension
                  for dato in data[dimension]:
                      if match_value(atom, dato):
                          break
                  # Pattern not found ---> good
                  else:
                      return True
              # Handle positive values (return True upon first successful match)
              else:
                  # Check each value for given dimension
                  for dato in data[dimension]:
                      if match_value(atom, dato):
                          # Pattern found ---> good
                          return True
          # No value matched the data
          return False
  
      def check_dimension(dimension, values):
          """ Check whether all values for given dimension match data """
          # E.g. dimension = 'tag', values = ['A, B', 'C', '-D']
          # Raise exception upon unknown dimension
          if dimension not in data:
              raise FilterError("Invalid filter '{0}'".format(dimension))
          # Every value must match at least one value for data
          return all([check_value(dimension, value) for value in values])
  
      def check_clause(clause):
          """ Split into literals and check whether all match """
          # E.g. clause = 'tag: A, B & tag: C & tag: -D'
          # Split into individual literals by dimension
          literals = dict()
          for literal in re.split("\s*&\s*", clause):
              # E.g. literal = 'tag: A, B'
              # Make sure the literal matches dimension:value format
              matched = re.match("^(.*)\s*:\s*(.*)$", literal)
              if not matched:
                  raise FilterError("Invalid filter '{0}'".format(literal))
              dimension, value = matched.groups()
              values = [value]
              # Append the literal value(s) to corresponding dimension list
              literals.setdefault(dimension, []).extend(values)
          # For each dimension all literals must match given data
          return all([check_dimension(dimension, values)
              for dimension, values in literals.items()])
  
      # Default to True if no filter given, bail out if weird data given
      if filter is None or filter == "": return True
      if not isinstance(data, dict):
          raise FilterError("Invalid data type '{0}'".format(type(data)))
  
      # Make sure that data dictionary contains lists of strings
      data = copy.deepcopy(data)
      try: # pragma: no cover
          for key in data:
              if isinstance(data[key], list):
                  data[key] = [unicode(item) for item in data[key]]
              else:
                  data[key] = [unicode(data[key])]
      except NameError: # pragma: no cover
          for key in data:
              if isinstance(data[key], list):
                  data[key] = [str(item) for item in data[key]]
              else:
                  data[key] = [str(data[key])]
      # Turn all data into lowercase if sensitivity is off
      if not sensitive:
          filter = filter.lower()
          lowered = dict()
          for key, values in data.items():
              lowered[key.lower()] = [value.lower() for value in values]
          data = lowered
  
      # At least one clause must be true
      return any([check_clause(clause)
              for clause in re.split("\s*\|\s*", filter)])