Example #1
0
    def get_stats(self, config, args):
        """Count number of bad tests.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Output variables
        counter = Counter()
        messages = set([])

        # Find all (sub)package names, from which one should not import directly
        packages = []
        for filename in get_source_filenames(config, 'py'):
            if filename.endswith('/__init__.py'):
                packages.append(filename[:-12].replace('/', '.'))

        # Loop all python and cython files
        for filename in get_source_filenames(config, 'py'):
            # Only consider relevant files
            if os.path.basename(filename).startswith('test_'):
                continue
            if filename.endswith('/__init__.py'):
                continue
            if 'data/examples/' in filename:
                continue
            # Look for bad imports
            with codecs.open(filename, encoding='utf-8') as f:
                for lineno, line in enumerate(f):
                    for package in packages:
                        if u'from %s import' % package in line and \
                           line != u'from %s import __version__\n' % package:
                            counter['Wrong imports in %s' % filename] += 1
                            text = 'Wrong import from %s' % package
                            messages.add(
                                Message(filename, lineno + 1, None, text))

        return counter, messages
    def get_stats(self, config, args):
        """Count number of bad tests.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Output variables
        counter = Counter()
        messages = set([])

        # Find all (sub)package names, from which one should not import directly
        packages = []
        for filename in get_source_filenames(config, 'py'):
            if filename.endswith('/__init__.py'):
                packages.append(filename[:-12].replace('/', '.'))

        # Loop all python and cython files
        for filename in get_source_filenames(config, 'py'):
            # Only consider relevant files
            if os.path.basename(filename).startswith('test_'):
                continue
            if filename.endswith('/__init__.py'):
                continue
            if 'data/examples/' in filename:
                continue
            # Look for bad imports
            with codecs.open(filename, encoding='utf-8') as f:
                for lineno, line in enumerate(f):
                    for package in packages:
                        if u'from %s import' % package in line and \
                           line != u'from %s import __version__\n' % package:
                            counter['Wrong imports in %s' % filename] += 1
                            text = 'Wrong import from %s' % package
                            messages.add(Message(filename, lineno + 1, None, text))

        return counter, messages
Example #3
0
    def get_stats(self, config, args):
        """Run tests using Pylint.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # get Pylint version
        command = ['pylint', '--version', '--rcfile=%s' % self.rcfile]
        version_info = ''.join(
            run_command(command, verbose=False)[0].split('\n')[:2])
        print 'USING              :', version_info

        # Collect python files not present in packages
        py_extra = get_source_filenames(config, 'py', unpackaged_only=True)

        # call Pylint
        command = ['pylint'] + config['py_packages'] \
                  + py_extra \
                  + ['--rcfile=%s' % self.rcfile,
                     '--ignore=%s' % (
                         ','.join(config['py_exclude'])),
                     '-j 2', ]
        output = run_command(command, has_failed=has_failed)[0]

        # parse the output of Pylint into standard return values
        lines = output.split('\n')[:-1]
        score = lines[-2].split()[6]
        print 'SCORE              :', score
        counter = Counter()
        messages = set([])
        for line in lines:
            # skip lines that don't contain error messages
            if '.py:' not in line:
                continue
            if line.startswith('Report'):
                break
            # extract error information
            msg_id, _keyword, location, msg = line.split(' ', 3)
            counter[msg_id] += 1
            filename, pos = location.split(':')
            lineno, charno = pos.split(',')
            lineno = int(lineno)
            charno = int(charno)
            if charno == 0:
                charno = None
            messages.add(
                Message(filename, lineno, charno, '%s %s' % (msg_id, msg)))
        return counter, messages
Example #4
0
    def get_stats(self, config, args):
        """Run tests using Pylint.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # get Pylint version
        command = ['pylint', '--version', '--rcfile=%s' % self.rcfile]
        version_info = ''.join(run_command(command, verbose=False)[0].split('\n')[:2])
        print 'USING              :', version_info

        # Collect python files not present in packages
        py_extra = get_source_filenames(config, 'py', unpackaged_only=True)

        # call Pylint
        command = ['pylint'] + config['py_packages'] \
                  + py_extra \
                  + ['--rcfile=%s' % self.rcfile,
                     '--ignore=%s' % (
                         ','.join(config['py_exclude'])),
                     '-j 2', ]
        output = run_command(command, has_failed=has_failed)[0]

        # parse the output of Pylint into standard return values
        lines = output.split('\n')[:-1]
        score = lines[-2].split()[6]
        print 'SCORE              :', score
        counter = Counter()
        messages = set([])
        for line in lines:
            # skip lines that don't contain error messages
            if '.py:' not in line:
                continue
            if line.startswith('Report'):
                break
            # extract error information
            msg_id, _keyword, location, msg = line.split(' ', 3)
            counter[msg_id] += 1
            filename, pos = location.split(':')
            lineno, charno = pos.split(',')
            lineno = int(lineno)
            charno = int(charno)
            if charno == 0:
                charno = None
            messages.add(Message(filename, lineno, charno, '%s %s' % (msg_id, msg)))
        return counter, messages
Example #5
0
    def get_stats(self, config, args):
        """Run tests using Cppcheck.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Look for custom cppcheck build
        fns_cppcheck = sorted(
            glob('%s/cached/cppcheck-*/cppcheck' % self.qaworkdir))
        if len(fns_cppcheck) > 0:
            binary = os.path.abspath(fns_cppcheck[-1])
        else:
            binary = 'cppcheck'
        print 'USING BINARY       :', binary

        # Get version
        command = [binary, '--version']
        print 'USING VERSION      :', run_command(command,
                                                  verbose=False)[0].strip()

        # Call Cppcheck
        command = [binary] + get_source_filenames(config, 'cpp') + \
                  ['-q', '--enable=all', '--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
        counter = Counter()
        messages = set([])
        for error in etree:
            if 'file' not in error.attrib:
                continue
            key = '%15s  %40s  %30s' % (
                error.attrib['severity'],
                error.attrib['file'].ljust(40),
                error.attrib['id'].ljust(30),
            )
            counter[key] += 1
            text = '%s %s %s' % (error.attrib['severity'], error.attrib['id'],
                                 error.attrib['msg'])
            messages.add(
                Message(error.attrib['file'], int(error.attrib['line']), None,
                        text))
        return counter, messages
    def get_stats(self, config, args):
        """Run tests using pydocstyle.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['pydocstyle', '--version']
        version = run_command(command, verbose=False)[0].strip()
        print 'USING              : pydocstyle', version

        # Call pydocstyle in the directories containing Python code. All files will be
        # checked, including test files. Missing docstrings are ignored because they are
        # detected by PyLint in a better way.
        command = ['pydocstyle', '--match=.*\\.py',
                   '--add-ignore=D100,D101,D102,D103,D104,D105'] + \
                  config['py_packages'] + \
                  get_source_filenames(config, 'py', unpackaged_only=True)
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the standard output of pydocstyle
        counter = Counter()
        messages = set([])
        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()

                key = '%s %s' % (code, filename)
                message = Message(filename, int(lineno), None,
                                  '%s %s' % (code, description))

                counter[key] += 1
                messages.add(message)
        return counter, messages
Example #7
0
    def get_stats(self, config, args):
        """Run tests using Cppcheck.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Look for custom cppcheck build
        fns_cppcheck = sorted(glob('%s/cached/cppcheck-*/cppcheck' % self.qaworkdir))
        if len(fns_cppcheck) > 0:
            binary = os.path.abspath(fns_cppcheck[-1])
        else:
            binary = 'cppcheck'
        print 'USING BINARY       :', binary

        # Get version
        command = [binary, '--version']
        print 'USING VERSION      :', run_command(command, verbose=False)[0].strip()

        # Call Cppcheck
        command = [binary] + get_source_filenames(config, 'cpp') + \
                  ['-q', '--enable=all', '--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
        counter = Counter()
        messages = set([])
        for error in etree:
            if 'file' not in error.attrib:
                continue
            key = '%15s  %40s  %30s' % (
                error.attrib['severity'],
                error.attrib['file'].ljust(40),
                error.attrib['id'].ljust(30),
            )
            counter[key] += 1
            text = '%s %s %s' % (error.attrib['severity'], error.attrib['id'], error.attrib['msg'])
            messages.add(Message(error.attrib['file'], int(error.attrib['line']),
                                 None, text))
        return counter, messages
    def get_stats(self, config, args):
        """Run tests using pydocstyle.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['pydocstyle', '--version']
        version = run_command(command, verbose=False)[0].strip()
        print 'USING              : pydocstyle', version

        # Call pydocstyle in the directories containing Python code. All files will be
        # checked, including test files. Missing docstrings are ignored because they are
        # detected by PyLint in a better way.
        command = ['pydocstyle', '--match=.*\\.py',
                   '--add-ignore=D100,D101,D102,D103,D104,D105'] + \
                  config['py_packages'] + \
                  get_source_filenames(config, 'py', unpackaged_only=True)
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the standard output of pydocstyle
        counter = Counter()
        messages = set([])
        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()

                key = '%s %s' % (code, filename)
                message = Message(filename, int(lineno), None, '%s %s' % (code, description))

                counter[key] += 1
                messages.add(message)
        return counter, messages
Example #9
0
    def get_stats(self, config):
        """Run tests using pydocstyle.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ["pydocstyle", "--version"]
        version = run_command(command, verbose=False)[0].strip()
        print "USING              : pydocstyle", version

        # Call pydocstyle in the directories containing Python code. All files will be
        # checked, including test files. Missing tests are ignored (D103) because they are
        # already detected by PyLint in a better way.
        command = (
            ["pydocstyle", "--match=.*\\.py", "--add-ignore=D103"]
            + config["py_packages"]
            + get_source_filenames(config, "py", unpackaged_only=True)
        )
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the standard output of pydocstyle
        counter = Counter()
        messages = set([])
        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()

                key = "%s %s" % (code, filename)
                message = Message(filename, int(lineno), None, "%s %s" % (code, description))

                counter[key] += 1
                messages.add(message)
        return counter, messages
Example #10
0
    def get_stats(self, config, args):
        """Run tests using cpplint.py.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        print 'USING              : cpplint.py update #456'

        # Call cpplint
        command = [
            self.cpplint_file, '--linelength=100', '--filter=-runtime/int'
        ]
        command += get_source_filenames(config, 'cpp')
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the output of cpplint into standard return values
        counter = Counter()
        messages = set([])
        for line in output.split('\n')[:-1]:
            if line.startswith('Done') or line.startswith('Total'):
                continue
            words = line.split()
            filename, lineno = words[0].split(':')[:2]
            description = ' '.join(words[1:-2])
            tag = words[-2]
            priority = words[-1]

            counter['%3s %-30s' % (priority, tag)] += 1
            messages.add(
                Message(filename, int(lineno), None,
                        '%s %s %s' % (priority, tag, description)))

        return counter, messages
    def get_stats(self, config, args):
        """Run tests using cpplint.py.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        print 'USING              : cpplint.py update #456'

        # Call cpplint
        command = [self.cpplint_file, '--linelength=100', '--filter=-runtime/int']
        command += get_source_filenames(config, 'cpp')
        output = run_command(command, has_failed=has_failed)[1]

        # Parse the output of cpplint into standard return values
        counter = Counter()
        messages = set([])
        for line in output.split('\n')[:-1]:
            if line.startswith('Done') or line.startswith('Total'):
                continue
            words = line.split()
            filename, lineno = words[0].split(':')[:2]
            description = ' '.join(words[1:-2])
            tag = words[-2]
            priority = words[-1]

            counter['%3s %-30s' % (priority, tag)] += 1
            messages.add(Message(filename, int(lineno), None, '%s %s %s' % (
                priority, tag, description)))

        return counter, messages
Example #12
0
    def get_stats(self, config, args):
        """Run tests using pydocstyle.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['pydocstyle', '--version']
        version = run_command(command, verbose=False)[0].strip()
        print 'USING              : pydocstyle', version

        default_match = '({0})'.format(config['default_match'])
        output = ''
        # apply trapdoor with custom configurations first
        for custom_config in config['custom'].values():
            match = custom_config['match']
            ignore = custom_config['ignore']
            # keep track of files that have been selected already
            # (this will be used to select files that have not been included in the configurations)
            default_match = '(?!{0}){1}'.format(match, default_match)
            # Call pydocstyle in the directories containing Python code. All files will be
            # checked, including test files. Missing docstrings are ignored because they are
            # detected by PyLint in a better way.
            output += run_command(
                [
                    'pydocstyle', '--match={0}'.format(match),
                    '--add-ignore={0}'.format(ignore)
                ] + config['py_packages'] +
                get_source_filenames(config, 'py', unpackaged_only=True),
                has_failed=has_failed)[1]
        # run trapdoor with default configuration on all the files that have not been tested yet
        output += run_command(
            [
                'pydocstyle', '--match={0}'.format(default_match),
                '--add-ignore={0}'.format(config['default_ignore'])
            ] + config['py_packages'] +
            get_source_filenames(config, 'py', unpackaged_only=True),
            has_failed=has_failed)[1]

        # Parse the standard output of pydocstyle
        counter = Counter()
        messages = set([])
        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()

                key = '%s %s' % (code, filename)
                message = Message(filename, int(lineno), None,
                                  '%s %s' % (code, description))

                counter[key] += 1
                messages.add(message)
        return counter, messages
Example #13
0
    def get_stats(self, config):
        """Count number of bad tests.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Output variables
        counter = Counter()
        messages = set([])

        # 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 = {}
        for filename in get_source_filenames(config, "py"):
            # Skip irrelevant files
            if filename.endswith("/__init__.py"):
                continue
            if os.path.basename(filename).startswith("test_"):
                continue
            # remove extension and replace / by .
            modulename = filename[: filename.rfind(".")].replace("/", ".")

            # Skip if this modulename is not part of a package
            in_package = False
            for packagename in config["py_packages"]:
                if modulename.startswith(packagename):
                    in_package = True
                    break
            if not in_package:
                continue

            # 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(modulename)
                        if name in config["py_invalid_names"]:
                            counter["Invalid name in namespace"] += 1
                            messages.add(Message(filename, None, None, "Invalid name in namespace: %s" % name))
            else:
                counter["Missing __all__"] += 1
                messages.add(Message(filename, None, None, "Missing __all__"))

        # Detect collisions
        for name, modules in namespace.iteritems():
            if len(modules) > 1:
                counter["Namespace collision"] += 1
                text = "Name '%s' found in modules %s" % (name, " ".join(modules))
                messages.add(Message(None, None, None, text))
        return counter, messages
Example #14
0
    def get_stats(self, config, args):
        """Count number of bad tests.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Output variables
        counter = Counter()
        messages = set([])

        # 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 = {}
        for filename in get_source_filenames(config, 'py'):
            # Skip irrelevant files
            if filename.endswith('/__init__.py'):
                continue
            if os.path.basename(filename).startswith('test_'):
                continue
            # remove extension and replace / by .
            modulename = filename[:filename.rfind('.')].replace('/', '.')

            # Skip if this modulename is not part of a package
            in_package = False
            for packagename in config['py_packages']:
                if modulename.startswith(packagename):
                    in_package = True
                    break
            if not in_package:
                continue

            # 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(modulename)
                        if name in config['py_invalid_names']:
                            counter['Invalid name in namespace'] += 1
                            messages.add(Message(filename, None, None,
                                                 'Invalid name in namespace: %s' % name))
            else:
                counter['Missing __all__'] += 1
                messages.add(Message(filename, None, None, 'Missing __all__'))

        # Detect collisions
        for name, modules in namespace.iteritems():
            if len(modules) > 1:
                counter['Namespace collision'] += 1
                text = 'Name \'%s\' found in modules %s' % (name, ' '.join(modules))
                messages.add(Message(None, None, None, text))
        return counter, messages
    def get_stats(self, config, args):
        """Run tests using Pylint.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # get default rcfile
        qatooldir = os.path.dirname(os.path.abspath(__file__))
        default_rc_file = os.path.join(self.qaworkdir, config['default_rc'])
        # FIXME: not too sure if this should be in prepare
        shutil.copy(os.path.join(qatooldir, os.path.basename(default_rc_file)), default_rc_file)

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

        # Collect python files (pylint ignore is quite bad.. need to ignore manually)
        py_extra = get_source_filenames(config, 'py', unpackaged_only=True)

        def get_filenames(file_or_dir, exclude=tuple()):
            """Recursively finds all of the files within the given file or directory.

            Avoids the files and directories specified in exclude.

            Parameters
            ----------
            file_or_dir : str
                File or directory
            exclude : tuple
                Files or directories to ignore

            Returns
            -------
            list of files as a relative path
            """
            output = []
            if os.path.isfile(file_or_dir) and file_or_dir not in exclude:
                output.append(file_or_dir)
            elif os.path.isdir(file_or_dir):
                for dirpath, _dirnames, filenames in os.walk(file_or_dir):
                    # check if directory is allowed
                    if any(os.path.samefile(dirpath, i) for i in exclude):
                        continue
                    for filename in filenames:
                        # check if filename is allowed
                        if (os.path.splitext(filename)[1] != '.py' or
                                filename in exclude or
                                any(os.path.samefile(os.path.join(dirpath, filename), i)
                                    for i in exclude)):
                            continue
                        output.append(os.path.join(dirpath, filename))
            return output

        output = ''
        exclude_files = []
        # run pylint test using each configuration
        for custom_config in config['custom'].values():
            rc_file = os.path.join(self.qaworkdir, custom_config['rc'])
            shutil.copy(os.path.join(qatooldir, os.path.basename(rc_file)), rc_file)

            # collect files
            py_files = []
            for custom_file in custom_config['files']:
                py_files.extend(get_filenames(custom_file))

            # call Pylint
            output += run_command(['pylint'] +
                                  py_files +
                                  ['--rcfile={0}'.format(rc_file),
                                   '-j 2', ],
                                  has_failed=has_failed)[0]
            # exclude directories/files
            exclude_files.extend(py_files)
        # get files that have not been run
        py_files = []
        for py_file in config['py_packages'] + py_extra:
            py_files.extend(get_filenames(py_file, exclude=exclude_files + config['py_exclude']))
        # call Pylint
        output += run_command(['pylint'] +
                              py_files +
                              ['--rcfile={0}'.format(default_rc_file),
                               '-j 2', ],
                              has_failed=has_failed)[0]

        # parse the output of Pylint into standard return values
        lines = output.split('\n')[:-1]
        score = lines[-2].split()[6]
        print 'SCORE              :', score
        counter = Counter()
        messages = set([])
        for line in lines:
            # skip lines that don't contain error messages
            if '.py:' not in line:
                continue
            if line.startswith('Report'):
                break
            # extract error information
            msg_id, _keyword, location, msg = line.split(' ', 3)
            counter[msg_id] += 1
            filename, pos = location.split(':')
            lineno, charno = pos.split(',')
            lineno = int(lineno)
            charno = int(charno)
            if charno == 0:
                charno = None
            messages.add(Message(filename, lineno, charno, '%s %s' % (msg_id, msg)))
        return counter, messages