Esempio n. 1
0
class DennisBear:
    """
    Lints your translated PO and POT files!

    Check multiple lint rules on all the strings in the PO file
    generating a list of errors and a list of warnings.

    See http://dennis.readthedocs.io/en/latest/linting.html for
    list of all error codes.

    http://dennis.readthedocs.io/
    """

    LANGUAGES = {'po', 'pot'}
    REQUIREMENTS = {PipRequirement('dennis', '0.8'),
                    # Workaround for https://github.com/willkg/dennis/issues/91
                    PipRequirement('click', '6.6')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Syntax'}

    @staticmethod
    def create_arguments(filename, file, config_file, allow_untranslated=True):
        """
        :param allow_untranslated: Set to false to display unchanged
                                   translation warning.
        """
        if allow_untranslated:
            return ('lint', filename, '--excluderules', 'W302')
        else:
            return ('lint', filename)
Esempio n. 2
0
class PyUnusedCodeBear(LocalBear):
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('autoflake', '0.6.6')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Unused Code'}

    def run(self, filename, file, remove_all_unused_imports: bool = False):
        """
        Detects unused code. By default this functionality is limited to:

        - Unneeded pass statements.
        - Unneeded builtin imports.

        :param remove_all_unused_imports:
            True removes all unused imports - might have side effects
        """

        corrected = autoflake.fix_code(
            ''.join(file),
            additional_imports=None,
            remove_all_unused_imports=remove_all_unused_imports,
            remove_unused_variables=True).splitlines(True)

        for diff in Diff.from_string_arrays(file, corrected).split_diff():
            yield Result(self,
                         'This file contains unused source code.',
                         affected_code=(diff.range(filename), ),
                         diffs={filename: diff})
Esempio n. 3
0
class SpellCheckBear:
    """
    Lints files to check for incorrect spellings using ``scspell``.

    scspell is a spell checker for source code.
    When applied to a code written in most popular programming languages
    while using most typical naming conventions, this algorithm will
    usually catch many errors without an annoying false positive rate.

    In an effort to catch more spelling errors, scspell is able to
    check each file against a set of dictionary words selected
    specifically for that file.

    See <https://pypi.python.org/pypi/scspell3k> for more information.
    """
    LANGUAGES = {'Natural Language'}
    REQUIREMENTS = {PipRequirement('scspell3k', '2.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    ASCIINEMA_URL = 'https://asciinema.org/a/87753'
    CAN_DETECT = {'Spelling'}

    @staticmethod
    def create_arguments(filename, file, config_file):
        return '--report-only', filename
Esempio n. 4
0
class CPPLintBear:
    """
    Check C++ code for Google's C++ style guide.

    For more information, consult <https://github.com/theandrewdavis/cpplint>.
    """

    LANGUAGES = {'C++'}
    REQUIREMENTS = {PipRequirement('cpplint', '1.3')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Formatting'}

    @staticmethod
    def create_arguments(filename,
                         file,
                         config_file,
                         max_line_length: int = 79,
                         cpplint_ignore: typed_list(str) = (),
                         cpplint_include: typed_list(str) = ()):
        """
        :param max_line_length:
            Maximum number of characters for a line.
        :param cpplint_ignore:
            List of checkers to ignore.
        :param cpplint_include:
            List of checkers to explicitly enable.
        """
        ignore = ','.join('-' + part.strip() for part in cpplint_ignore)
        include = ','.join('+' + part.strip() for part in cpplint_include)
        return ('--filter=' + ignore + ',' + include,
                '--linelength=' + str(max_line_length), filename)
class reSTLintBear(LocalBear):
    LANGUAGES = {'reStructuredText'}
    REQUIREMENTS = {PipRequirement('restructuredtext-lint', '1.0.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Formatting', 'Syntax'}

    def run(self, filename, file):
        """
        Lints reStructuredText.
        """
        content = ''.join(file)
        errors = lint(content)

        for error in errors:
            severity = {
                1: RESULT_SEVERITY.INFO,
                2: RESULT_SEVERITY.NORMAL,
                3: RESULT_SEVERITY.MAJOR,
                4: RESULT_SEVERITY.MAJOR
            }.get(error.level, RESULT_SEVERITY.NORMAL)
            yield Result.from_values(self,
                                     error.message,
                                     file=filename,
                                     line=error.line,
                                     debug_msg=error.full_message,
                                     severity=severity)
Esempio n. 6
0
class HTMLLintBear:
    """
    Check HTML source code for invalid or misformatted code.

    See also <https://pypi.python.org/pypi/html-linter>.
    """

    _html_lint = which('html_lint.py')

    LANGUAGES = {'HTML', 'Jinja2', 'PHP'}
    REQUIREMENTS = {PipRequirement('html-linter', '0.4.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Syntax', 'Formatting'}

    @staticmethod
    def create_arguments(filename, file, config_file, use_spaces: bool,
                         htmllint_ignore: typed_list(str)=[]):
        """
        :param htmllint_ignore: List of checkers to ignore.
        :param use_spaces: True if spaces are to be used instead of tabs.
        """
        additional_ignore = []
        if not use_spaces:
            additional_ignore.append('tabs')

        ignore = ','.join(part.strip()
                          for part in htmllint_ignore + additional_ignore)

        return HTMLLintBear._html_lint, '--disable=' + ignore, filename
Esempio n. 7
0
class YAMLLintBear:
    """
    Check yaml code for errors and possible problems.

    You can read more about capabilities at
    <http://yamllint.readthedocs.org/en/latest/rules.html>.
    """

    LANGUAGES = {'YAML'}
    REQUIREMENTS = {PipRequirement('yamllint', '1.12.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Syntax', 'Formatting'}

    @staticmethod
    def generate_config(
        filename,
        file,
        document_start: bool = None,
        max_line_length: int = 80,
    ):
        """
        :param document_start:
            Use this rule to require or forbid the use of document start
            marker (---).
        :param max_line_length:
            Maximum number of characters for a line, the newline character
            being excluded.
        """
        yamllint_configs = {
            'extends': 'default',
            'rules': {
                'document-start': 'disable' if document_start is None else {
                    'present': document_start
                },
                'line-length': {
                    'max': max_line_length,
                },
            },
        }

        return yaml.dump(yamllint_configs)

    @staticmethod
    def create_arguments(
        filename,
        file,
        config_file,
        yamllint_config: str = '',
    ):
        """
        :param yamllint_config: Path to a custom configuration file.
        """
        args = ('-f', 'parsable', filename)
        if yamllint_config:
            args += ('--config-file=' + yamllint_config, )
        else:
            args += ('--config-file=' + config_file, )
        return args
Esempio n. 8
0
class HTMLLintBear:
    """
    Check HTML source code for invalid or misformatted code.

    See also <https://pypi.python.org/pypi/html-linter>.
    """

    _html_lint = which('html_lint.py')

    LANGUAGES = {'HTML'}
    REQUIREMENTS = {PipRequirement('html-linter', '0.3')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Syntax', 'Formatting'}

    @staticmethod
    def create_arguments(filename,
                         file,
                         config_file,
                         htmllint_ignore: typed_list(str) = ()):
        """
        :param htmllint_ignore: List of checkers to ignore.
        """
        ignore = ','.join(part.strip() for part in htmllint_ignore)
        return HTMLLintBear._html_lint, '--disable=' + ignore, filename
Esempio n. 9
0
class BanditBear:
    """
    Performs security analysis on Python source code, utilizing the ``ast``
    module from the Python standard library.
    """
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('bandit', '1.2')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Security'}

    @staticmethod
    def create_arguments(
        filename,
        file,
        config_file,
        bandit_skipped_tests: typed_list(str) = ('B105', 'B106', 'B107',
                                                 'B404', 'B603', 'B606',
                                                 'B607'),
    ):
        """
        :param bandit_skipped_tests:
            The IDs of the tests ``bandit`` shall not perform. You can get
            information about the available builtin codes at
            https://github.com/openstack/bandit#usage.
        """
        args = (filename, '-f', 'json')

        if bandit_skipped_tests:
            args += ('-s', ','.join(bandit_skipped_tests))

        return args

    severity_map = {
        'HIGH': RESULT_SEVERITY.MAJOR,
        'MEDIUM': RESULT_SEVERITY.NORMAL,
        'LOW': RESULT_SEVERITY.INFO
    }

    confidence_map = {'HIGH': 90, 'MEDIUM': 70, 'LOW': 50}

    def process_output(self, output, filename, file):
        output = json.loads(output)

        for error in output['errors']:
            yield Result.from_values(origin=self,
                                     file=filename,
                                     severity=RESULT_SEVERITY.MAJOR,
                                     message=error['reason'])

        for issue in output['results']:
            yield Result.from_values(
                origin=issue['test_id'],
                file=filename,
                message=issue['issue_text'],
                severity=self.severity_map[issue['issue_severity']],
                confidence=self.confidence_map[issue['issue_confidence']],
                line=issue['line_range'][0],
                end_line=issue['line_range'][-1])
Esempio n. 10
0
class RSTcheckBear:
    """
    Check syntax of ``reStructuredText`` and code blocks
    nested within it.

    Check <https://pypi.python.org/pypi/rstcheck> for more information.
    """

    LANGUAGES = {'reStructuredText'}
    REQUIREMENTS = {PipRequirement('rstcheck', '3.1')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Formatting', 'Syntax'}
    ASCIINEMA_URL = 'https://asciinema.org/a/8ntlaqubk2qkrn9mm0dh07rlk?speed=2'

    @staticmethod
    def create_arguments(filename,
                         file,
                         config_file,
                         code_block_language_ignore: list = ()):
        """
        :param code_block_language_ignore:
            Comma seperated list for languages of which code blocks
            should be ignored. Code block of following languages
            can be detected: ``bash``, ``c``, ``cpp``, ``json``,
            ``python``, ``rst``.
        """
        args = ()
        if code_block_language_ignore:
            args = ('--ignore-language=' +
                    ','.join(code_block_language_ignore), )
        return args + (filename, )
Esempio n. 11
0
class HTTPoliceLintBear:
    """
    HTTPolice is a linter for HTTP requests and responses. It checks them for
    conformance to standards and best practices.
    """
    LANGUAGES = {'HAR'}
    REQUIREMENTS = {PipRequirement('HTTPolice', '0.5.2')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Formatting', 'Syntax'}
    SEE_MORE = 'https://github.com/vfaronov/httpolice'

    @staticmethod
    def create_arguments(
        filename,
        file,
        config_file,
        httpolice_silence_ids: typed_list(str) = [],
    ):
        """
        :param httpolice_silence_ids:
            Silences the given list of notice IDs. You can get more information
            about the available notices and their IDs at
            https://httpolice.readthedocs.io/en/stable/notices.html.
        """
        args = '-i', 'har', filename
        if httpolice_silence_ids:
            args += tuple('-s=' + part for part in httpolice_silence_ids)
        return args
Esempio n. 12
0
class CPPCleanBear:
    """
    Find problems in C++ source code that slow down development in large code
    bases. This includes finding unused code, among other features.

    Read more about available routines at
    <https://github.com/myint/cppclean#features>.
    """

    LANGUAGES = {'C++'}
    REQUIREMENTS = {PipRequirement('cppclean', '0.12.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Smell', 'Unused Code', 'Security'}

    @staticmethod
    def create_arguments(
            filename,
            file,
            config_file,
            include_paths: typed_list(str) = (),
    ):
        args = [filename]
        for include_path in include_paths:
            args.append('--include-path')
            args.append(include_path)

        return args
Esempio n. 13
0
class CMakeLintBear:
    """
    Check CMake code for syntactical or formatting issues.

    For more information consult <https://github.com/richq/cmake-lint>.
    """
    LANGUAGES = {'CMake'}
    REQUIREMENTS = {PipRequirement('cmakelint', '1.3')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Syntax', 'Formatting'}

    @staticmethod
    def create_arguments(
        filename,
        file,
        config_file,
        cmakelint_config: path = '',
    ):
        """
        :param cmakelint_config: The location of the cmakelintrc config file.
        """
        args = ()
        if cmakelint_config:
            args += ('--config=' + cmakelint_config, )
        return args + (filename, )
Esempio n. 14
0
class PyDocStyleBear:
    """
    Checks python docstrings.
    """
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('pydocstyle', '1.1')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Formatting', 'Documentation'}

    def create_arguments(self, filename, file, config_file,
                         pydocstyle_select: typed_list(str)=(),
                         pydocstyle_ignore: typed_list(str)=()):
        """
        :param pydocstyle_select:
            List of checked errors by specifying which errors to check for.
            Can't be used together with ``pydocstyle_ignore``.
        :param pydocstyle_ignore:
            List of checked errors by specifying which errors to ignore. Can't
            be used together with ``pydocstyle_select``.
        """
        args = (filename,)
        if pydocstyle_ignore and pydocstyle_select:
            self.err('The arguments pydocstyle_select and pydocstyle_ignore '
                     'are both given but mutually exclusive.')
            return
        elif pydocstyle_ignore:
            ignore = ','.join(part.strip() for part in pydocstyle_ignore)
            args += ('--ignore=' + ignore,)
        elif pydocstyle_select:
            select = ','.join(part.strip() for part in pydocstyle_select)
            args += ('--select=' + select,)

        return args
Esempio n. 15
0
class ClangBear(LocalBear):
    LANGUAGES = {'C', 'C++', 'Objective-C', 'Objective-C++', 'OpenMP',
                 'OpenCL', 'CUDA'}
    # Depends on libclang-py3, which is a dependency of coala
    REQUIREMENTS = {PipRequirement('libclang-py3', '3.4.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_FIX = {'Variable Misuse', 'Syntax'}

    check_prerequisites = classmethod(clang_available)

    def run(self, filename, file, clang_cli_options: typed_list(str)=None):
        """
        Check code for syntactical or semantical problems using Clang.

        This bear supports automatic fixes.

        :param clang_cli_options: Any options that will be passed through to
                                  Clang.
        """
        index = Index.create()
        diagnostics = index.parse(
            filename,
            args=clang_cli_options,
            unsaved_files=[(filename, ''.join(file))]).diagnostics
        for diag in diagnostics:
            severity = {0: RESULT_SEVERITY.INFO,
                        1: RESULT_SEVERITY.INFO,
                        2: RESULT_SEVERITY.NORMAL,
                        3: RESULT_SEVERITY.MAJOR,
                        4: RESULT_SEVERITY.MAJOR}.get(diag.severity)
            affected_code = tuple(sourcerange_from_clang_range(range)
                                  for range in diag.ranges)

            diffs = None
            fixits = list(diag.fixits)
            if len(fixits) > 0:
                # FIXME: coala doesn't support choice of diffs, for now
                # append first one only, often there's only one anyway
                diffs = {filename: diff_from_clang_fixit(fixits[0], file)}

                # No affected code yet? Let's derive it from the fix!
                if len(affected_code) == 0:
                    affected_code = diffs[filename].affected_code(filename)

            # Still no affected code? Position is the best we can get...
            if len(affected_code) == 0 and diag.location.file is not None:
                affected_code = (SourceRange.from_values(
                    diag.location.file.name,
                    diag.location.line,
                    diag.location.column),)

            yield Result(
                self,
                diag.spelling,
                severity=severity,
                affected_code=affected_code,
                diffs=diffs)
Esempio n. 16
0
class MypyBear:
    """
    Type-checks your Python files!

    Checks optional static typing using the mypy tool.
    See <http://mypy.readthedocs.io/en/latest/basics.html> for info on how to
    add static typing.
    """

    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    AUTHORS = {'Petr Viktorin'}
    REQUIREMENTS = {PipRequirement('mypy', '==0.590')}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    ASCIINEMA_URL = 'https://asciinema.org/a/90736'

    # This detects typing errors, which is pretty unique -- it doesn't
    # make sense to add a category for it.
    CAN_DETECT = set()

    @add_param_docs(FLAG_MAP)
    def create_arguments(
        self,
        filename,
        file,
        config_file,
        language: language = language('Python 3'),
        python_version: str = None,
        allow_untyped_functions: bool = True,
        allow_untyped_calls: bool = True,
        check_untyped_function_bodies: bool = False,
        strict_optional: bool = False,
    ):
        """
        :param language:
            Set to ``Python`` or ``Python 3`` to check Python 3.x source.
            Use ``Python 2`` for Python 2.x.
        :param python_version:
            Set the specific Python version, e.g. ``3.5``.
        """
        args = ['-m', 'mypy']
        if 'python 2' in language:
            args.append('--py2')
        elif 'python 3' not in language:
            # Ideally, this would fail the check, but there's no good
            # way to fail from create_arguments.
            # See https://github.com/coala/coala/issues/2573
            self.err(
                'Language needs to be "Python", "Python 2" or "Python 3". '
                'Assuming Python 3.')
        if python_version:
            args.extend(['--python-version', python_version])
        loc = locals()
        args.extend(flag.arg for name, flag in FLAG_MAP.items()
                    if flag.want_flag(loc[name]))
        args.append(filename)
        return args
Esempio n. 17
0
class SQLFormatBear(LocalBear):
    LANGUAGES = {'SQL'}
    REQUIREMENTS = {PipRequirement('sqlparse', '0.2.4')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Syntax'}

    def run(self,
            filename,
            file,
            keyword_case: str = 'upper',
            identifier_case: str = 'lower',
            strip_comments: bool = False,
            reindent: bool = True,
            indent_tabs: bool = False,
            indent_width: int = 4):
        """
        Linter for SQL language based on sqlparse.
        More info: https://github.com/andialbrecht/sqlparse

        :param keyword_case:
            Changes how keywords are formatted.
            Allowed values are `upper`, `lower` and `capitalize`.
        :param identifier_case:
            Changes how identifiers are formatted.
            Allowed values are `upper`, `lower`, and `capitalize`.
        :param strip_comments:
            If True comments are removed from the statements.
        :param reindent:
            If True the indentations of the statements are changed.
        :param indent_tabs:
            If True tabs instead of spaces are used for indentation.
        :param indent_width:
            The width of the indentation.
        """
        corrected = sqlparse.format(
            ''.join(file),
            keyword_case=keyword_case,
            identifier_case=identifier_case,
            strip_comments=strip_comments,
            reindent=reindent,
            indent_tabs=indent_tabs,
            indent_width=indent_width,
        )

        corrected = corrected.splitlines(True)
        diffs = Diff.from_string_arrays(file, corrected).split_diff()

        for diff in diffs:
            yield Result(self,
                         'The code format can be improved.',
                         affected_code=(diff.range(filename), ),
                         diffs={filename: diff})
Esempio n. 18
0
class PEP8Bear(LocalBear):
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('autopep8', '1.2')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_FIX = {'Formatting'}
    ASCIINEMA_URL = 'https://asciinema.org/a/165394'

    @deprecate_settings(indent_size='tab_width')
    def run(
            self,
            filename,
            file,
            max_line_length: int = 79,
            indent_size: int = SpacingHelper.DEFAULT_TAB_WIDTH,
            pep_ignore: typed_list(str) = (),
            pep_select: typed_list(str) = (),
            local_pep8_config: bool = False,
    ):
        """
        Detects and fixes PEP8 incompliant code. This bear will not change
        functionality of the code in any way.

        :param max_line_length:   Maximum number of characters for a line.
                                  When set to 0 allows infinite line length.
        :param indent_size:       Number of spaces per indentation level.
        :param pep_ignore:        A list of errors/warnings to ignore.
        :param pep_select:        A list of errors/warnings to exclusively
                                  apply.
        :param local_pep8_config: Set to true if autopep8 should use a config
                                  file as if run normally from this directory.
        """
        if not max_line_length:
            max_line_length = sys.maxsize

        options = {
            'ignore': pep_ignore,
            'select': pep_select,
            'max_line_length': max_line_length,
            'indent_size': indent_size
        }

        corrected = autopep8.fix_code(''.join(file),
                                      apply_config=local_pep8_config,
                                      options=options).splitlines(True)

        diffs = Diff.from_string_arrays(file, corrected).split_diff()

        for diff in diffs:
            yield Result(self,
                         'The code does not comply to PEP8.',
                         affected_code=(diff.range(filename), ),
                         diffs={filename: diff})
    def __init__(self,
                 package,
                 version="",
                 imports: list=None):
        """
        Constructs a new ``PythonImportRequirement`` using the
        ``PipRequirement``

        :param package:
                A string with the name of the package to be installed.
        :param version:
                A version string. Leave empty to specify latest version.
        :param imports:
                A list of module attributes(dotted path representation) to be
                imported. Leave empty to specify package name as required
                import.
        """
        if not imports:
            self.imports = [package]
        else:
            self.imports = imports
        PipRequirement.__init__(self, package, version)
class PycodestyleBear:
    """
    A wrapper for the tool ``pycodestyle`` formerly known as ``pep8``.
    """
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('pycodestyle', '2.2')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Formatting'}

    @staticmethod
    def create_arguments(
            filename,
            file,
            config_file,
            pycodestyle_ignore: typed_list(str) = ('E121', 'E123', 'E126',
                                                   'E133', 'E226', 'E241',
                                                   'E242', 'E704', 'W503'),
            pycodestyle_select: typed_list(str) = (),
            max_line_length: int = 79):
        """
        :param pycodestyle_ignore:
            Comma separated list of errors to ignore.
            See ``pydocstyle`` documentation for a complete list of errors.
        :param pycodestyle_select:
            Comma separated list of errors to detect. If given only
            these errors are going to be detected.
            See ``pydocstyle`` documentation for a complete list of errors.
        :param max_line_length:
            Limit lines to this length. Allows infinite line length when
            set to 0.
        """
        arguments = [r'--format=%(row)d %(col)d %(code)s %(text)s']

        if not max_line_length:
            max_line_length = sys.maxsize

        if pycodestyle_ignore:
            ignore = ','.join(part.strip() for part in pycodestyle_ignore)
            arguments.append('--ignore=' + ignore)

        if pycodestyle_select:
            select = ','.join(part.strip() for part in pycodestyle_select)
            arguments.append('--select=' + select)

        arguments.append('--max-line-length=' + str(max_line_length))

        arguments.append(filename)

        return arguments
Esempio n. 21
0
class RadonBear(LocalBear):
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('radon', '1.4.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Complexity'}

    def run(
            self,
            filename,
            file,
            radon_ranks_info: typed_list(str) = (),
            radon_ranks_normal: typed_list(str) = ('C', 'D'),
            radon_ranks_major: typed_list(str) = ('E', 'F'),
    ):
        """
        Uses radon to compute complexity of a given file.

        :param radon_ranks_info:   The ranks (given by radon) to
                                   treat as severity INFO.
        :param radon_ranks_normal: The ranks (given by radon) to
                                   treat as severity NORMAL.
        :param radon_ranks_major:  The ranks (given by radon) to
                                   treat as severity MAJOR.
        """
        severity_map = {
            RESULT_SEVERITY.INFO: radon_ranks_info,
            RESULT_SEVERITY.NORMAL: radon_ranks_normal,
            RESULT_SEVERITY.MAJOR: radon_ranks_major
        }
        for visitor in radon.complexity.cc_visit(''.join(file)):
            rank = radon.complexity.cc_rank(visitor.complexity)
            severity = None
            for result_severity, rank_list in severity_map.items():
                if rank in rank_list:
                    severity = result_severity
            if severity is None:
                continue

            col = visitor.col_offset if visitor.col_offset else None
            visitor_range = SourceRange.from_values(filename, visitor.lineno,
                                                    col, visitor.endline)
            message = '{} has a cyclomatic complexity of {}'.format(
                visitor.name, rank)

            yield Result(self,
                         message,
                         severity=severity,
                         affected_code=(visitor_range, ))
Esempio n. 22
0
class YAMLLintBear:
    """
    Check yaml code for errors and possible problems.

    You can read more about capabilities at
    <http://yamllint.readthedocs.org/en/latest/rules.html>.
    """

    LANGUAGES = {'YAML'}
    REQUIREMENTS = {PipRequirement('yamllint', '1.5')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Syntax', 'Formatting'}

    @staticmethod
    def generate_config(filename, file, document_start: bool = False):
        """
        :param document_start:
            Use this rule to require or forbid the use of document start
            marker (---).
        """
        yamllint_configs = {
            'extends': 'default',
            'rules': {
                'document-start': {
                    'present': False
                }
            }
        }
        if document_start:
            yamllint_configs['rules']['document-start']['present'] = True

        return yaml.dump(yamllint_configs)

    @staticmethod
    def create_arguments(filename,
                         file,
                         config_file,
                         yamllint_config: str = ''):
        """
        :param yamllint_config: Path to a custom configuration file.
        """
        args = ('-f', 'parsable', filename)
        if yamllint_config:
            args += ('--config-file=' + yamllint_config, )
        else:
            args += ('--config-file=' + config_file, )
        return args
Esempio n. 23
0
class ProseLintBear:
    """
    Lints the file using `proselint <https://github.com/amperser/proselint>`__.
    Works only with English language text.
    """
    LANGUAGES = {'Natural Language'}
    REQUIREMENTS = {PipRequirement('proselint', '0.7')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Spelling', 'Syntax', 'Formatting', 'Grammar'}

    @staticmethod
    def create_arguments(filename, file, config_file):
        return filename,
Esempio n. 24
0
class PyUnusedCodeBear(
        LocalBear,
        aspects={
            'fix': [
                UnusedImport,
                UnusedLocalVariable,
            ]},
        languages=['Python'],
        ):
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('autoflake', '0.7')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Unused Code'}

    @map_setting_to_aspect(
        remove_all_unused_imports=UnusedImport.remove_non_standard_import,
        remove_unused_variables=UnusedLocalVariable)
    def run(self, filename, file,
            remove_all_unused_imports: bool = True,
            remove_unused_variables: bool = True,
            ):
        """
        Detects unused code. By default this functionality is limited to:

        - Unneeded pass statements.
        - All unused imports - might have side effects
        - Unused variables - might have side effects

        :param remove_all_unused_imports:
            ``False`` removes only unused builtin imports
        :param remove_unused_variables:
            ``False`` keeps unused variables
        """
        corrected = autoflake.fix_code(
                       ''.join(file),
                       additional_imports=None,
                       remove_all_unused_imports=remove_all_unused_imports,
                       remove_unused_variables=remove_unused_variables
                       ).splitlines(True)

        for diff in Diff.from_string_arrays(file, corrected).split_diff():
            yield Result(self,
                         'This file contains unused source code.',
                         affected_code=(diff.range(filename),),
                         diffs={filename: diff},
                         aspect=Redundancy('py'))
Esempio n. 25
0
class VultureBear(GlobalBear):
    LANGUAGES = {'Python', 'Python 3'}
    REQUIREMENTS = {PipRequirement('vulture', '0.25.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    ASCIINEMA_URL = 'https://asciinema.org/a/82256'
    CAN_DETECT = {'Unused Code'}
    SEE_MORE = 'https://github.com/jendrikseipp/vulture'

    def run(self):
        """
        Check Python code for unused variables and functions using `vulture`.
        """
        filenames = list(self.file_dict.keys())
        return _find_unused_code(filenames)
Esempio n. 26
0
class PyLintBear:
    """
    Checks the code with pylint. This will run pylint over each file
    separately.
    """
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('pylint', '1.7.2')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {
        'Unused Code', 'Formatting', 'Duplication', 'Security', 'Syntax'
    }

    @staticmethod
    def create_arguments(
        filename,
        file,
        config_file,
        pylint_disable: typed_list(str) = None,
        pylint_enable: typed_list(str) = None,
        pylint_cli_options: str = '',
        pylint_rcfile: str = '',
    ):
        """
        :param pylint_disable:     Disable the message, report, category or
                                   checker with the given id(s).
        :param pylint_enable:      Enable the message, report, category or
                                   checker with the given id(s).
        :param pylint_cli_options: Any command line options you wish to be
                                   passed to pylint.
        :param pylint_rcfile:      The rcfile for PyLint.
        """
        args = ('--reports=n', '--persistent=n',
                '--msg-template="L{line}C{column}: {msg_id} - {msg}"')
        if pylint_disable:
            args += ('--disable=' + ','.join(pylint_disable), )
        if pylint_enable:
            args += ('--enable=' + ','.join(pylint_enable), )
        if pylint_cli_options:
            args += tuple(shlex.split(pylint_cli_options))
        if pylint_rcfile:
            args += ('--rcfile=' + pylint_rcfile, )
        else:
            args += ('--rcfile=' + os.devnull, )

        return args + (filename, )
Esempio n. 27
0
class PyFlakesBear:
    """
    Checks Python files for errors using ``pyflakes``.

    See https://github.com/PyCQA/pyflakes for more info.
    """
    LANGUAGES = {'Python', 'Python 3'}
    REQUIREMENTS = {PipRequirement('pyflakes', '1.4.0')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    ASCIINEMA_URL = 'https://asciinema.org/a/92503'
    CAN_DETECT = {'Syntax', 'Unused Code', 'Undefined Element'}

    @staticmethod
    def create_arguments(filename, file, config_file):
        return filename,
Esempio n. 28
0
class VintBear:
    """
    Check vimscript code for possible style problems.

    See <https://github.com/Kuniwak/vint> for more information.
    """

    LANGUAGES = {'VimScript'}
    REQUIREMENTS = {PipRequirement('vim-vint', '0.3')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Formatting'}

    @staticmethod
    def create_arguments(filename, file, config_file):
        return filename,
Esempio n. 29
0
class PyCommentedCodeBear(LocalBear):
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('eradicate', '0.1')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Commented Code'}

    def run(self, filename, file):
        """
        Detects commented out source code in Python.
        """
        corrected = tuple(eradicate.filter_commented_out_code(''.join(file)))

        for diff in Diff.from_string_arrays(file, corrected).split_diff():
            yield Result(self,
                         'This file contains commented out source code.',
                         affected_code=(diff.range(filename),),
                         diffs={filename: diff})
Esempio n. 30
0
class PycodestyleBear:
    """
    A wrapper for the tool ``pycodestyle`` formerly known as ``pep8``.
    """
    LANGUAGES = {'Python', 'Python 2', 'Python 3'}
    REQUIREMENTS = {PipRequirement('pycodestyle', '2.2')}
    AUTHORS = {'The coala developers'}
    AUTHORS_EMAILS = {'*****@*****.**'}
    LICENSE = 'AGPL-3.0'
    CAN_DETECT = {'Formatting'}

    @staticmethod
    def create_arguments(
            filename, file, config_file,
            pycodestyle_ignore: str='E121, E123, E126, E133, E226, E241, '
                                    'E242, E704, W503',
            pycodestyle_select: str='',
            max_line_length: int=79):
        """
        :param pycodestyle_ignore:
            Comma separated list of errors to ignore.
            See ``pydocstyle`` documentation for a complete list of errors.
        :param pycodestyle_select:
            Comma separated list of errors to detect. If given only
            these errors are going to be detected.
            See ``pydocstyle`` documentation for a complete list of errors.
        :param max_line_length:
            Limit lines to this length.
        """
        arguments = [r'--format=%(row)d %(col)d %(code)s %(text)s']

        if pycodestyle_ignore:
            arguments.append('--ignore=' + pycodestyle_ignore)

        if pycodestyle_select:
            arguments.append('--select=' + pycodestyle_select)

        arguments.append('--max-line-length=' + str(max_line_length))

        arguments.append(filename)

        return arguments
Esempio n. 31
0
def main(debug=False):
    configure_logging()

    args = None  # to have args variable in except block when parse_args fails
    try:
        # Note: We parse the args here once to check whether to show bears or
        # not.
        args = default_arg_parser().parse_args()
        if args.debug:
            req_ipdb = PipRequirement('ipdb')
            if not req_ipdb.is_installed():
                logging.error('--debug flag requires ipdb. '
                              'You can install it with:\n%s',
                              ' '.join(req_ipdb.install_command()))
                sys.exit(13)

        if debug or args.debug:
            args.log_level = 'DEBUG'

        # Defer imports so if e.g. --help is called they won't be run
        from coalib.coala_modes import (
            mode_format, mode_json, mode_non_interactive, mode_normal)
        from coalib.output.ConsoleInteraction import (
            show_bears, show_language_bears_capabilities)

        console_printer = ConsolePrinter(print_colored=not args.no_color)
        configure_logging(not args.no_color)

        if args.show_bears:
            from coalib.settings.ConfigurationGathering import get_all_bears
            kwargs = {}
            if args.bears:
                kwargs['bear_globs'] = args.bears
            filtered_bears = get_all_bears(**kwargs)
            if args.filter_by_language:
                logging.warning(
                    "'--filter-by-language ...' is deprecated. "
                    "Use '--filter-by language ...' instead.")
                if args.filter_by is None:
                    args.filter_by = []
                args.filter_by.append(['language'] + args.filter_by_language)
            if args.filter_by:
                # Each iteration of the following loop applies
                # filters one by one provided as arguments
                try:
                    args.filter_by = filter_vector_to_dict(args.filter_by)
                    filtered_bears = apply_filters(
                        args.filter_by, filtered_bears)
                except (InvalidFilterException, NotImplementedError) as ex:
                    # If filter is not available or is unusable
                    console_printer.print(ex)
                    return 2

            local_bears, global_bears = filtered_bears
            show_bears(local_bears,
                       global_bears,
                       args.show_description or args.show_details,
                       args.show_details,
                       console_printer,
                       args)

            return 0
        elif args.show_capabilities:
            from coalib.collecting.Collectors import (
                filter_capabilities_by_languages)
            local_bears, _ = apply_filter('language', args.show_capabilities)
            capabilities = filter_capabilities_by_languages(
                local_bears, args.show_capabilities)
            show_language_bears_capabilities(capabilities, console_printer)

            return 0

        if args.json:
            return mode_json(args, debug=debug)

    except BaseException as exception:  # pylint: disable=broad-except
        if not isinstance(exception, SystemExit):
            if args and args.debug:
                import ipdb
                with ipdb.launch_ipdb_on_exception():
                    raise

            if debug:
                raise

        return get_exitcode(exception)

    if args.format:
        return mode_format(args, debug=debug)

    if args.non_interactive:
        return mode_non_interactive(console_printer, args, debug=debug)

    return mode_normal(console_printer, None, args, debug=debug)