@classmethod
def _partition(cls, expr_str):
    """Partitions expr_str. See examples below.

        >>> Expression._partition('abc || efg')
        ('abc', '||', 'efg')
        >>> Expression._partition('(a== b) &&(( b!=c)||d )')
        ('a== b', '&&', '( b!=c)||d')
        >>> Expression._partition('!(1 <= 2)')
        ('', '!', '(1 <= 2)')
        >>> Expression._partition('')
        ('', '', '')
        """
    expr_str = expr_str.strip()
    if expr_str.startswith('!'):
        return '', '!', expr_str[1:].strip()
    left_startpos, left_endpos = cls._scan_brackets(expr_str)
    if left_startpos >= 0:
        left_str = expr_str[left_startpos + 1:left_endpos].strip()
        if left_endpos + 1 == len(expr_str):
            return left_str, '', ''
        op_startpos = left_endpos + 1
        while expr_str[op_startpos] == ' ':
            op_startpos += 1
        for op_endpos in range(op_startpos + 1, op_startpos - 1, -1):
            op_str = expr_str[op_startpos:op_endpos + 1]
            if op_str in cls.operators:
                break
        else:
            raise ValueError(
                "expression syntax error: expected operator at '%s'" %
                expr_str[op_startpos:])
    else:
        for op_startpos, ch in enumerate(expr_str):
            if ch == ' ':
                continue
            if ch == '(' or ch == ')':
                raise ValueError(
                    "expression syntax error: expected operator before '%s'" %
                    expr_str[op_startpos:])
            for op_endpos in range(op_startpos + 1, op_startpos - 1, -1):
                op_str = expr_str[op_startpos:op_endpos + 1]
                if op_str in cls.operators:
                    break
            else:
                continue
            break
        else:
            left_str = expr_str.strip()
            op_str = ''
            right_str = ''
            return left_str, op_str, right_str
        left_str = expr_str[:op_startpos].strip()
    right_startpos, right_endpos = cls._scan_brackets(expr_str, op_endpos + 1)
    if right_startpos >= 0:
        right_str = expr_str[right_startpos + 1:right_endpos].strip()
        if expr_str[right_endpos + 1:] and not expr_str[right_endpos + 1:
            ] == ' ':
            for op in cls.operators:
                if expr_str.find(op) != -1:
                    break
            else:
                raise ValueError(
                    "expression syntax error: unexpected trailing characters '%s'"
                     % expr_str[right_endpos + 1:])
            right_str = expr_str[op_endpos + 1:].strip()
    else:
        right_str = expr_str[op_endpos + 1:].strip()
        if '(' in right_str or ')' in right_str:
            raise ValueError(
                "expression syntax error: unexpected brackets in '%s'" %
                right_str)
    return left_str, op_str, right_str
