Parses constraints into a (sql, params) tuple.

    :constraints: can either be a dict, an enumerable with a sql string and params, an enumerable of
    2- or 3-tuples, or just a string.

    If is_assignment is false, transform_op will be called on each comparison operator.

def parse_constraints(self, constraints, joiner=" and ", *, is_assignment=False, comp="="):
    """Parses constraints into a (sql, params) tuple.

    :constraints: can either be a dict, an enumerable with a sql string and params, an enumerable of
    2- or 3-tuples, or just a string.

    If is_assignment is false, transform_op will be called on each comparison operator.
    """
    if constraints is None:
      return (None, None)
    elif isinstance(constraints, dict):
      return (
        self.join_comparisons(constraints, joiner, is_assignment=is_assignment, comp=comp),
        self.get_params(constraints))
    elif isinstance(constraints, (list, tuple)):
      if len(constraints) == 2 and isinstance(constraints[0], str):
        return (constraints[0], constraints[1])
      else:
        return (
          self.join_comparisons(constraints, joiner, is_assignment=is_assignment, comp=comp),
          self.get_params(constraints))
    elif isinstance(constraints, str):
      return (constraints, None)
    else:
      raise TypeError(
        "constraints must be None, str, dict, list or tuple. Is {0}".format(
          type(constraints).__name__))