def analyze(source):
    '''Analyze the source code and return a namedtuple with the following
    fields:

        * **loc**: The number of lines of code (total)
        * **lloc**: The number of logical lines of code
        * **sloc**: The number of source lines of code (not necessarily
            corresponding to the LLOC)
        * **comments**: The number of Python comment lines
        * **multi**: The number of lines which represent multi-line strings
        * **blank**: The number of blank lines (or whitespace-only ones)

    The equation :math:`sloc + blanks = loc` should always hold.
    Multiline strings are not counted as comments, since, to the Python
    interpreter, they are not comments but strings.
    '''
    lloc = comments = multi = blank = 0

    # Cast source code into an array, devoid of blank lines
    source_array = [line.strip() for line in source.split('\n') if line]
    sloc = len(source_array)
    loc, single_comments, multi = remove_python_documentation(source_array)
    lines = iter(source.splitlines())
    for lineno, line in enumerate(lines, 1):
        line = line.strip()
        if not line:
            blank += 1
            continue
        try:
            # Process a logical line that spans on multiple lines
            tokens, _, _ = _get_all_tokens(line, lines)
        except __HOLE__:
            raise SyntaxError('SyntaxError at line: {0}'.format(lineno))
        # Add the comments
        comments += list(map(TOKEN_NUMBER, tokens)).count(COMMENT)
        # Process a logical line
        # Split it on semicolons because they increase the number of logical
        # lines
        for sub_tokens in _split_tokens(tokens, OP, ';'):
            lloc += _logical(sub_tokens)
    return Module(loc, lloc, sloc, comments, multi, blank, single_comments)
dataset/ETHPy150Open rubik/radon/radon/raw.py/analyze