import re


def expr_identifiers(expr):
    """Scan `expr` for identifiers,  assume helper functions are in mixed or lowercase.

    Returns [ identifier_in_expr, ...]

    >>> expr_identifiers("((EXP_TYPE)in(['NRS_MSASPEC','NRS_FIXEDSLIT','NRS_BRIGHTOBJ','NRS_IFU']))")
    ['EXP_TYPE']

    >>> expr_identifiers("(len(SCI_ARRAY.SHAPE)==2)")
    ['SCI_ARRAY']

    >>> expr_identifiers("_")
    []

    >>> expr_identifiers("200121")
    []

    >>> expr_identifiers("(not(META_SUBARRAY_NAME)in(['GENERIC','N/A']))")
    ['META_SUBARRAY_NAME']
    """
    # First match identifiers including quoted strings and dotted attribute paths.
    candidates = [ key.group(0) for key in re.finditer(r"['\"\.A-Z0-9_a-z]+", expr)]
    # Next reject strings with quotes in them,  or lower case or mixed case
    no_quotes = [key for key in candidates if re.match(r"^[A-Z0-9_\.]+$", key)]
    no_dots = [key.split(".")[0] for key in no_quotes]
    no_numbers = [key for key in no_dots if not re.match(r"\d+", key)]
    no_underscores = [key for key in no_numbers if key != "_"]
    return no_underscores