Ejemplo n.º 1
0
def linter_header(linter_config, files_lines):
    """Linter for checking source file headers.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified).
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    # Load the header file as a set of lines
    header_lines = list(config['extra'])
    with codecs.open(config['header'], encoding='utf-8') as f:
        for line in f:
            header_lines.append((config['comment'] + line).strip())

    # Get all relevant filenames
    filenames = [
        filename for filename in files_lines
        if matches_filefilter(filename, config['filefilter'])
    ]

    # Loop all files and check in the header each file.
    messages = []
    for filename in filenames:
        with codecs.open(filename, encoding='utf-8') as f:
            iterator = iter(enumerate(f))
            header_counter = 0
            while header_counter < len(header_lines):
                try:
                    lineno, line = next(iterator)
                except StopIteration:
                    break
                if lineno == 0 and line.startswith(
                        '#!') and config['shebang'] is not None:
                    if line[:-1] != config['shebang']:
                        messages.append(
                            Message(
                                filename, lineno, None,
                                'Shebang line should be {}.'.format(
                                    config['shebang'])))
                else:
                    expected = header_lines[header_counter]
                    if line[:-1] != expected:
                        messages.append(
                            Message(filename, lineno, None,
                                    'Line should be: {}'.format(expected)))
                    header_counter += 1
    return messages
Ejemplo n.º 2
0
def run_namespace(config, filenames):
    """Linter for checking namespace Python namespace collisions.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # Make sure we test the source tree and not some locally installed copy of HORTON.
    sys.path.insert(0, '.')

    # Find all module names and load namespaces
    namespace = {}
    messages = []
    for filename in filenames:
        # remove extension and replace / by .
        modulename = os.path.splitext(filename)[0].replace('/', '.')

        # Check all public names of the module.
        module = importlib.import_module(modulename)
        names = dir(module)
        if '__all__' in names:
            for name in names:
                if name in module.__all__:
                    namespace.setdefault(name, []).append(filename)
                    if name in config['forbidden']:
                        messages.append(
                            Message(
                                filename, None, None,
                                'Invalid name in namespace: {0}'.format(name)))
        else:
            messages.append(Message(filename, None, None, 'Missing __all__'))

    # Fix sys.path
    del sys.path[0]

    # Detect collisions
    for name, sourcefiles in namespace.items():
        if len(sourcefiles) > 1:
            text = "Name '{0}' found in more than one module: {1}".format(
                name, ' '.join(sourcefiles))
            messages.append(Message(sourcefiles[0], None, None, text))
    return messages
Ejemplo n.º 3
0
def run_whitespace(_config, filenames):
    """Linter for checking whitespace conventions.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # Loop over all files and check whitespace in each file.
    messages = []
    for filename in filenames:
        with codecs.open(filename, encoding='utf-8') as f:
            line = None
            lineno = -1
            for lineno, line in enumerate(f):
                # Check for tabs
                charno = line.find('\t')
                if charno >= 0:
                    messages.append(
                        Message(filename, lineno + 1, charno + 1, 'tab'))
                # Check for carriage return
                charno = line.find('\r')
                if charno >= 0:
                    messages.append(
                        Message(filename, lineno + 1, charno + 1,
                                'carriage return'))
                # Check for trailing whitespace
                if line[:-1] != line.rstrip():
                    messages.append(
                        Message(filename, lineno + 1, None,
                                'trailing whitespace'))
            # Perform some checks on the last line
            if line is not None:
                if len(line.strip()) == 0:
                    messages.append(
                        Message(filename, lineno + 1, None,
                                'trailing empty line'))
                if not line.endswith("\n"):
                    messages.append(
                        Message(filename, lineno + 1, None,
                                'last line missing \\n'))
    return messages
Ejemplo n.º 4
0
def run_header(config, filenames):
    """Linter for checking source file headers.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # Load the header file as a set of lines
    header_lines = list(config['extra'])
    with codecs.open(config['header'], encoding='utf-8') as f:
        for line in f:
            header_lines.append((config['comment'] + line).strip())

    # Loop all files and check in the header each file.
    messages = []
    for filename in filenames:
        with codecs.open(filename, encoding='utf-8') as f:
            iterator = iter(enumerate(f))
            header_counter = 0
            while header_counter < len(header_lines):
                try:
                    lineno, line = next(iterator)
                except StopIteration:
                    break
                if lineno == 0 and line.startswith(
                        '#!') and config['shebang'] is not None:
                    if line[:-1] != config['shebang']:
                        messages.append(
                            Message(
                                filename, lineno + 1, None,
                                'Shebang line should be {}.'.format(
                                    config['shebang'])))
                else:
                    expected = header_lines[header_counter]
                    if line[:-1] != expected:
                        messages.append(
                            Message(filename, lineno + 1, None,
                                    'Line should be: {}'.format(expected)))
                    header_counter += 1
    return messages
Ejemplo n.º 5
0
def run_import(config, filenames):
    """Linter for checking import statements.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # Loop all python and cython files
    messages = []
    if len(config['packages']) > 0:
        for filename in filenames:
            # Look for bad imports
            with codecs.open(filename, encoding='utf-8') as f:
                for lineno, line in enumerate(f):
                    for package in config['packages']:
                        # skip version import
                        if line == u'from {0} import __version__\n'.format(package):
                            continue
                        if u'from {0} import'.format(package) in line:
                            text = 'Wrong import from {0}'.format(package)
                            messages.append(Message(filename, lineno+1, None, text))
    return messages
Ejemplo n.º 6
0
def run_flake8(config, filenames):
    """Linter for checking flake8 results.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # get flake8 version
    command = ['flake8', '--version']
    version_info = run_command(command, verbose=False)[0]
    print('USING              : {0}'.format(version_info))

    messages = []
    if len(filenames) > 0:
        command = ['flake8'] + filenames
        if config['config'] is not None:
            command += ['--config={0}'.format(config['config'])]
        output = run_command(command, has_failed=_has_failed)[0]
        if len(output) > 0:
            for line in output.splitlines():
                words = line.split(':')
                messages.append(
                    Message(words[0], int(words[1]), int(words[2]),
                            words[3].strip()))
    return messages
Ejemplo n.º 7
0
def run_import(config, filenames):
    """Linter for checking import statements.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # Loop all python and cython files
    messages = []
    if len(config['packages']) > 0:
        for filename in filenames:
            try:
                _check_file(filename, config, messages)
            except UnicodeDecodeError as err:
                messages.append(Message(filename, None, None, str(err)))
    return messages
Ejemplo n.º 8
0
def run_header(config, filenames):
    """Linter for checking source file headers.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # Load the header file as a set of lines
    header_lines = list(config['extra'])
    with codecs.open(config['header'], encoding='utf-8') as f:
        for line in f:
            header_lines.append((config['comment'] + line).strip())

    # Loop all files and check in the header each file.
    messages = []
    for filename in filenames:
        try:
            _check_file(filename, config, header_lines, messages)
        except UnicodeDecodeError as err:
            messages.append(Message(filename, None, None, str(err)))
    return messages
Ejemplo n.º 9
0
def linter_pylint(linter_config, files_lines):
    """Linter for checking pylint results.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified).
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    # get Pylint version
    command = [
        'pylint', '--version', '--rcfile={0}'.format(config['pylintrc'])
    ]
    version_info = ''.join(
        run_command(command, verbose=False)[0].split('\n')[:2])
    print('USING              : {0}'.format(version_info))

    # Get all relevant filenames
    filenames = [
        filename for filename in files_lines
        if matches_filefilter(filename, config['filefilter'])
    ]

    def has_failed(returncode, _stdout, _stderr):
        """Determine if pylint ran correctly."""
        return not 0 <= returncode < 32

    messages = []
    if len(filenames) > 0:
        command = ['pylint'] + filenames
        command += [
            '--rcfile={0}'.format(config['pylintrc']), '--jobs=2',
            '--output-format=json'
        ]
        output = run_command(command, has_failed=has_failed)[0]
        if len(output) > 0:
            for plmap in json.loads(output):
                charno = plmap['column']
                if charno == 0:
                    charno = None
                messages.append(
                    Message(
                        plmap['path'], plmap['line'], charno,
                        '{0} {1}'.format(plmap['symbol'], plmap['message'])))
    return messages
Ejemplo n.º 10
0
def linter_cppcheck(linter_config, files_lines):
    """Linter for cppcheck.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
        Not supported
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified)
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    # Get version
    print('USING VERSION      : {0}'.format(
        run_command(['cppcheck', '--version'], verbose=False)[0].strip()))

    # Get the relevant filenames
    filenames = [
        filename for filename in files_lines
        if matches_filefilter(filename, config['filefilter'])
    ]

    messages = []
    if len(filenames) > 0:
        # Call Cppcheck
        command = (['cppcheck'] + filenames + [
            '-q', '--enable=all', '--language=c++', '--std=c++11', '--xml',
            '--suppress=missingIncludeSystem', '--suppress=unusedFunction'
        ])
        xml_str = run_command(command)[1]
        etree = ElementTree.fromstring(xml_str)

        # Parse the output of Cppcheck into standard return values
        for error in etree:
            if 'file' not in error.attrib:
                continue
            # key = '{:<15}  {:<40}  {:<30}' % (error.attrib['severity'],
            #                                   error.attrib['file'],
            #                                   error.attrib['id'])
            text = '{} {} {}'.format(error.attrib['severity'],
                                     error.attrib['id'], error.attrib['msg'])
            messages.append(
                Message(error.attrib['file'], int(error.attrib['line']), None,
                        text))

    return messages
Ejemplo n.º 11
0
def linter_pydocstyle(linter_config, files_lines):
    """Linter for checking pydocstyle results.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified).
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    # get pydocstyle version
    command = ['pydocstyle', '--version']
    version_info = run_command(command, verbose=False)[0]
    print('USING              : {0}'.format(version_info))

    # Get all relevant filenames
    filenames = [
        filename for filename in files_lines
        if matches_filefilter(filename, config['filefilter'])
    ]

    def has_failed(returncode, _stdout, _stderr):
        """Determine if pydocstyle ran correctly."""
        return not 0 <= returncode < 2

    messages = []
    if len(filenames) > 0:
        command = ['pydocstyle'] + filenames
        command += ['--config={0}'.format(config['config'])]
        output = run_command(command, has_failed=has_failed)[0]
        lines = output.split('\n')[:-1]
        while len(lines) > 0:
            if 'WARNING: ' in lines[0]:
                lines.pop(0)
            else:
                words = lines.pop(0).split()
                filename, lineno = words[0].split(':')
                code, description = lines.pop(0).split(':', 1)
                code = code.strip()
                description = description.strip()
                messages.append(
                    Message(filename, int(lineno), None,
                            '%s %s' % (code, description)))
    return messages
Ejemplo n.º 12
0
def linter_doxygen(linter_config, files_lines):
    """Linter using doxygen to find undocumented cpp.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
        Not supported
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified)
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    command = ['doxygen', '--version']
    print('USING              : doxygen',
          run_command(command, verbose=False)[0].strip())

    # Get the relevant filenames
    filenames = [
        filename for filename in files_lines
        if matches_filefilter(filename, config['filefilter'])
    ]

    messages = []
    if len(filenames) > 0:
        # Call doxygen in the doc subdirectory, mute output because it only confuses
        command = ['doxygen', '-']
        stdin = DOXYGEN_CONFIG.format(' '.join(filenames))
        print('STDIN              :')
        print(stdin)
        output = run_command(command, cwd=config['root'], stdin=stdin)[1]

        # Parse the file doxygen_warnings
        prefix = os.getcwd() + '/'

        lines = output.split('\n')
        for line in lines:
            if line.startswith('~~WARN~~'):
                location, description = line[9:].split(None, 1)
                filename, lineno = location.split(':')[:2]
                if filename.startswith(prefix):
                    filename = filename[len(prefix):]
                message = Message(filename, int(lineno), None, description)
                messages.append(message)
    return messages
Ejemplo n.º 13
0
def _check_file(filename: str, config: dict, header_lines: List[str],
                messages: List[str]):
    """Look for bad filename headers.

    Parameters
    ----------
    filename
        File to be checked
    config
        Dictionary with configuration of the linters.
    header_lines
        The expected header.
    messages
        A list of messages to append to. (output arg)

    """
    with codecs.open(filename, encoding='utf-8') as f:
        iterator = iter(enumerate(f))
        header_counter = 0
        while header_counter < len(header_lines):
            try:
                lineno, line = next(iterator)
            except StopIteration:
                break
            if lineno == 0 and line.startswith(
                    '#!') and config['shebang'] is not None:
                if line[:-1] != config['shebang']:
                    messages.append(
                        Message(
                            filename, lineno + 1, None,
                            'Shebang line should be {}.'.format(
                                config['shebang'])))
            else:
                expected = header_lines[header_counter]
                if line[:-1] != expected:
                    messages.append(
                        Message(filename, lineno + 1, None,
                                'Line should be: {}'.format(expected)))
                header_counter += 1
Ejemplo n.º 14
0
def _check_file(filename: str, messages: List[str]):
    """Look for white-space issues.

    Parameters
    ----------
    filename
        File to be checked
    messages
        A list of messages to append to. (output arg)

    """
    with codecs.open(filename, encoding='utf-8') as f:
        line = None
        lineno = -1
        for lineno, line in enumerate(f):
            # Check for tabs
            charno = line.find('\t')
            if charno >= 0:
                messages.append(
                    Message(filename, lineno + 1, charno + 1, 'tab'))
            # Check for carriage return
            charno = line.find('\r')
            if charno >= 0:
                messages.append(
                    Message(filename, lineno + 1, charno + 1,
                            'carriage return'))
            # Check for trailing whitespace
            if line[:-1] != line.rstrip():
                messages.append(
                    Message(filename, lineno + 1, None, 'trailing whitespace'))
        # Perform some checks on the last line
        if line is not None:
            if len(line.strip()) == 0:
                messages.append(
                    Message(filename, lineno + 1, None, 'trailing empty line'))
            if not line.endswith("\n"):
                messages.append(
                    Message(filename, lineno + 1, None,
                            'last line missing \\n'))
Ejemplo n.º 15
0
def linter_pycodestyle(linter_config, files_lines):
    """Linter for checking pycodestyle results.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified).
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    # get pycodestyle version
    command = ['pycodestyle', '--version']
    version_info = run_command(command, verbose=False)[0]
    print('USING              : {0}'.format(version_info))

    # Get all relevant filenames
    filenames = [
        filename for filename in files_lines
        if matches_filefilter(filename, config['filefilter'])
    ]

    def has_failed(returncode, _stdout, _stderr):
        """Determine if pycodestyle ran correctly."""
        return not 0 <= returncode < 2

    messages = []
    if len(filenames) > 0:
        command = ['pycodestyle'] + filenames
        command += ['--config={0}'.format(config['config'])]
        output = run_command(command, has_failed=has_failed)[0]
        if len(output) > 0:
            for line in output.splitlines():
                words = line.split(':')
                messages.append(
                    Message(words[0], int(words[1]), int(words[2]),
                            words[3].strip()))
    return messages
Ejemplo n.º 16
0
def linter_cpplint(linter_config, files_lines):
    """Linter for cpplint.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
        Not supported
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified)
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    # Get the relevant filenames
    filenames = [filename for filename in files_lines
                 if matches_filefilter(filename, config['filefilter'])]

    messages = []
    if len(filenames) > 0:
        # Call cpplint
        command = ([config['script'], '--linelength=100', '--filter=-runtime/int'] +
                   filenames)
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the output of cpplint into standard return values
        for line in output.split('\n')[:-1]:
            words = line.split()
            if len(words) == 0 or words[0].count(':') != 2:
                continue
            filename, lineno = words[0].split(':')[:2]
            description = ' '.join(words[1:-2])
            tag = words[-2]
            priority = words[-1]

            messages.append(Message(filename, int(lineno), None, '%s %s %s' % (
                priority, tag, description)))
    return messages
Ejemplo n.º 17
0
def run_cppcheck(_config, filenames):
    """Linter for cppcheck.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
        Not supported
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # Get version
    print('USING VERSION      : {0}'.format(
        run_command(['cppcheck', '--version'], verbose=False)[0].strip()))

    messages = []
    if len(filenames) > 0:
        # Call Cppcheck
        command = (['cppcheck'] + filenames +
                   ['-q', '--enable=all', '--language=c++', '--std=c++11', '--xml',
                    '--suppress=missingIncludeSystem', '--suppress=unusedFunction'])
        xml_str = run_command(command)[1]
        etree = ElementTree.fromstring(xml_str)

        # Parse the output of Cppcheck into standard return values
        for error in etree:
            if 'file' not in error.attrib:
                continue
            # key = '{:<15}  {:<40}  {:<30}' % (error.attrib['severity'],
            #                                   error.attrib['file'],
            #                                   error.attrib['id'])
            text = '{} {} {}'.format(
                error.attrib['severity'], error.attrib['id'], error.attrib['msg'])
            lineno = int(error.attrib['line'])
            if lineno == 0:
                lineno = None
            messages.append(Message(error.attrib['file'], lineno, None, text))
    return messages
Ejemplo n.º 18
0
def run_pydocstyle(config, filenames):
    """Linter for checking pydocstyle results.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # get pydocstyle version
    command = ['pydocstyle', '--version']
    version_info = run_command(command, verbose=False)[0]
    print('USING              : {0}'.format(version_info))

    def has_failed(returncode, _stdout, _stderr):
        """Determine if pydocstyle ran correctly."""
        return not 0 <= returncode < 2

    messages = []
    if len(filenames) > 0:
        command = ['pydocstyle'] + filenames
        if config['config'] is not None:
            command += ['--config={0}'.format(config['config'])]
        output = run_command(command, has_failed=has_failed)[0]
        lines = output.split('\n')[:-1]
        while len(lines) > 0:
            if 'WARNING: ' in lines[0]:
                lines.pop(0)
            else:
                words = lines.pop(0).split()
                filename, lineno = words[0].split(':')
                code, description = lines.pop(0).split(':', 1)
                code = code.strip()
                description = description.strip()
                messages.append(Message(
                    filename, int(lineno), None, '%s %s' % (code, description)))
    return messages
Ejemplo n.º 19
0
def run_pylint(config, filenames):
    """Linter for checking pylint results.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # get Pylint version
    command = ['pylint', '--version']
    version_info = ''.join(
        run_command(command, verbose=False)[0].split('\n')[:2])
    print('USING              : {0}'.format(version_info))

    def has_failed(returncode, _stdout, _stderr):
        """Determine if pylint ran correctly."""
        return not 0 <= returncode < 32

    messages = []
    if len(filenames) > 0:
        command = ['pylint'] + filenames
        command += ['--jobs=2', '--output-format=json']
        if config['config'] is not None:
            command += ['--rcfile={0}'.format(config['config'])]
        output = run_command(command, has_failed=has_failed)[0]
        if len(output) > 0:
            for plmap in json.loads(output):
                charno = plmap['column']
                if charno in [0, -1]:
                    charno = None
                messages.append(
                    Message(
                        plmap['path'], plmap['line'], charno,
                        '{0} {1}'.format(plmap['symbol'], plmap['message'])))
    return messages
Ejemplo n.º 20
0
def run_doxygen(_config, filenames):
    """Linter using doxygen to find undocumented cpp.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
        Not supported
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    command = ['doxygen', '--version']
    print('USING              : doxygen',
          run_command(command, verbose=False)[0].strip())

    messages = []
    if len(filenames) > 0:
        # Call doxygen in the doc subdirectory, mute output because it only confuses
        command = ['doxygen', '-']
        stdin = DOXYGEN_CONFIG.format(' '.join(filenames))
        print('STDIN              :')
        print(stdin)
        output = run_command(command, stdin=stdin)[1]

        # Parse the file doxygen_warnings
        prefix = os.getcwd() + '/'

        lines = output.split('\n')
        for line in lines:
            if line.startswith('~~WARN~~'):
                location, description = line[9:].split(None, 1)
                filename, lineno = location.split(':')[:2]
                if filename.startswith(prefix):
                    filename = filename[len(prefix):]
                message = Message(filename, int(lineno), None, description)
                messages.append(message)
    return messages
Ejemplo n.º 21
0
def linter_import(linter_config, files_lines):
    """Linter for checking import statements.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified).
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    # Get all relevant filenames
    filenames = [filename for filename in files_lines
                 if matches_filefilter(filename, config['filefilter'])]

    # Loop all python and cython files
    messages = []
    if len(config['packages']) > 0:
        for filename in filenames:
            # Look for bad imports
            with codecs.open(filename, encoding='utf-8') as f:
                for lineno, line in enumerate(f):
                    for package in config['packages']:
                        # skip version import
                        if line == u'from {0} import __version__\n'.format(package):
                            continue
                        if u'from {0} import'.format(package) in line:
                            text = 'Wrong import from {0}'.format(package)
                            messages.append(Message(filename, lineno+1, None, text))
    return messages
Ejemplo n.º 22
0
def run_cpplint(config, filenames):
    """Linter for cpplint.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
        Not supported
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    messages = []
    if len(filenames) > 0:
        # Call cpplint
        command = ([config['script'], '--linelength=100', '--filter=-runtime/int'] +
                   filenames)
        output = run_command(command, has_failed=_has_failed)[1]

        # Parse the output of cpplint into standard return values
        for line in output.split('\n')[:-1]:
            words = line.split()
            if len(words) == 0 or words[0].count(':') != 2:
                continue
            filename, lineno = words[0].split(':')[:2]
            description = ' '.join(words[1:-2])
            tag = words[-2]
            priority = words[-1]
            lineno = int(lineno)
            if lineno == 0:
                lineno = None
            messages.append(Message(filename, lineno, None, '%s %s %s' % (
                priority, tag, description)))
    return messages
Ejemplo n.º 23
0
def _check_file(filename: str, config: dict, messages: List[str]):
    """Look for bad imports in the given file.

    Parameters
    ----------
    filename
        File to be checked
    config
        Dictionary with configuration of the linters.
    messages
        A list of messages to append to. (output arg)

    """
    with codecs.open(filename, encoding='utf-8') as f:
        for lineno, line in enumerate(f):
            for package in config['packages']:
                # skip version import
                if line == u'from {0} import __version__\n'.format(package):
                    continue
                if u'from {0} import'.format(package) in line:
                    text = 'Wrong import from {0}'.format(package)
                    messages.append(Message(filename, lineno + 1, None, text))
Ejemplo n.º 24
0
def run_yamllint(config, filenames):
    """Linter for checking yamllint results.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # get yamllint version
    command = ['yamllint', '--version']
    version_info = run_command(command, verbose=False)[0]
    print('USING              : {0}'.format(version_info))

    def has_failed(returncode, _stdout, _stderr):
        """Determine if yamllint ran correctly."""
        return not 0 <= returncode < 2

    messages = []
    if len(filenames) > 0:
        command = ['yamllint', '-f', 'parsable'] + filenames
        if config['config'] is not None:
            command += ['-c', config['config']]
        output = run_command(command, has_failed=has_failed)[0]
        if len(output) > 0:
            for line in output.splitlines():
                words = line.split(':')
                messages.append(
                    Message(words[0], int(words[1]), int(words[2]),
                            words[3].strip()))
    return messages
Ejemplo n.º 25
0
def run_whitespace(_config, filenames):
    """Linter for checking whitespace conventions.

    Parameters
    ----------
    config : dict
        Dictionary that contains the configuration for the linter
    filenames : list
        A list of filenames to check

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    # Loop over all files and check whitespace in each file.
    messages = []
    for filename in filenames:
        try:
            _check_file(filename, messages)
        except UnicodeDecodeError as err:
            messages.append(Message(filename, None, None, str(err)))
    return messages
Ejemplo n.º 26
0
def test_message():
    # formatting
    msg1 = Message('test.txt', 1, 4, 'error')
    assert msg1.format(
    ) == '\x1b[1m\x1b[31m1:4      \x1b[0m \x1b[35mtest.txt\x1b[0m  error'
    assert msg1.format(color=False) == '1:4       test.txt  error'
    assert msg1.format(color=False) == str(msg1)
    msg2 = Message('test.txt', None, 4, 'error')
    assert msg2.format(
    ) == '\x1b[1m\x1b[31m-:4      \x1b[0m \x1b[35mtest.txt\x1b[0m  error'
    assert msg2.format(color=False) == '-:4       test.txt  error'
    msg3 = Message('test.txt', 1, None, 'error')
    assert msg3.format(
    ) == '\x1b[1m\x1b[31m1:-      \x1b[0m \x1b[35mtest.txt\x1b[0m  error'
    assert msg3.format(color=False) == '1:-       test.txt  error'
    msg4 = Message(None, 1, 3, 'error')
    assert msg4.format() == '\x1b[1m(nofile)\x1b[0m  error'
    assert msg4.format(color=False) == '(nofile)  error'
    # comparison
    assert msg1 > msg3
    assert msg3 > msg2
    assert msg2 > msg4
    with assert_raises(TypeError):
        print(msg1 < 1)
    # sorting
    msgs = sorted([msg1, msg2, msg3, msg4])
    assert msgs == [msg4, msg2, msg3, msg1]
    with assert_raises(TypeError):
        msgs.insert(0, 1)
        msgs.sort()
    # wrong arguments
    with assert_raises(TypeError):
        Message('foo', -1, 4, 'bar')
    with assert_raises(TypeError):
        Message('foo', 4, -1, 'bar')
    # indiff
    assert msg1.indiff({'test.txt': set([1])})
    assert msg1.indiff({'test.txt': None})
    assert not msg1.indiff({'test.txt': set([2])})
    assert msg2.indiff({'test.txt': set([1])})
    assert msg2.indiff({'test.txt': None})
    assert msg2.indiff({'test.txt': set([2])})
    assert not msg2.indiff({'foo.txt': set([2])})
Ejemplo n.º 27
0
def linter_namespace(linter_config, files_lines):
    """Linter for checking namespace Python namespace collisions.

    Parameters
    ----------
    linter_config : dict
        Dictionary that contains the configuration for the linter
    files_lines : dict
        Dictionary of filename to the set of line numbers (that have been modified).
        See `run_diff` function in `carboardlinter`.

    Returns
    -------
    messages : list
        The list of messages generated by the external linter.

    """
    config = DEFAULT_CONFIG.copy()
    config.update(linter_config)

    # Get all relevant filenames
    filenames = [
        filename for filename in files_lines
        if matches_filefilter(filename, config['filefilter'])
    ]

    # Make sure we test the source tree and not some locally installed copy of HORTON.
    sys.path.insert(0, '.')

    # Find all module names and load namespaces
    namespace = {}
    messages = []
    for filename in filenames:
        # remove extension and replace / by .
        modulename = os.path.splitext(filename)[0].replace('/', '.')

        # Check all public names of the module.
        module = importlib.import_module(modulename)
        names = dir(module)
        if '__all__' in names:
            for name in names:
                if name in module.__all__:
                    namespace.setdefault(name, []).append(filename)
                    if name in config['forbidden']:
                        messages.append(
                            Message(
                                filename, None, None,
                                'Invalid name in namespace: {0}'.format(name)))
        else:
            messages.append(Message(filename, None, None, 'Missing __all__'))

    # Fix sys.path
    del sys.path[0]

    # Detect collisions
    for name, filenames in namespace.items():
        if len(filenames) > 1:
            text = "Name '{0}' found in more than one module: {1}".format(
                name, ' '.join(filenames))
            messages.append(Message(filenames[0], None, None, text))
    return messages