Source code for flakeheaven._logic._extractors

# built-in
import ast
import re
from functools import partial
from importlib import import_module
from pathlib import Path
from typing import Dict, List


REX_CODE = re.compile(r'^[A-Z]{1,5}[0-9]{1,5}$')
ALIASES = {
    'flake8_bugbear': 'bugbear',
    'flake8_logging_format': 'logging_format',
}


[docs]class CollectStrings(ast.NodeVisitor): _strings: List[str]
[docs] def visit_Str(self, node): self._strings.append(node.s)
[docs]def get_messages(code: str, content: str) -> Dict[str, str]: root = ast.parse(content) CollectStrings._strings = [] collector = CollectStrings() collector.visit(root) messages = dict() for message in collector._strings: message_code, _, message_text = message.partition(' ') if not message_text: continue message_code = message_code.rstrip(':') if not REX_CODE.match(message_code): continue if code and not message_code.startswith(code): continue messages[message_code] = message_text return messages
[docs]def extract_default(name: str) -> Dict[str, str]: module = import_module(name) if module.__file__ is None: raise NotImplementedError(f'Unable to parse {name} module') content = Path(module.__file__).read_text() return get_messages(code='', content=content)
[docs]def extract(name) -> Dict[str, str]: name = name.replace('-', '_') name = ALIASES.get(name, name) function_name = 'extract_' + name # use ad-hoc extractor if available if function_name in globals(): return globals()[function_name]() # try to extract by default algorithm return extract_default(name)
# -- EXTRACTORS WITH A CUSTOM PATH -- #
[docs]def extract_flake8_absolute_import() -> Dict[str, str]: return extract_default(name='flake8_absolute_import.core')
[docs]def extract_flake8_aaa() -> Dict[str, str]: return extract_default(name='flake8_aaa.line_markers')
[docs]def extract_flake8_cognitive_complexity() -> Dict[str, str]: return extract_default(name='flake8_cognitive_complexity.checker')
[docs]def extract_flake8_variables_names() -> Dict[str, str]: return extract_default(name='flake8_variables_names.checker')
[docs]def extract_logging_format() -> Dict[str, str]: return extract_default(name='logging_format.violations')
[docs]def extract_flake8_sql() -> Dict[str, str]: return extract_default(name='flake8_sql.linter')
[docs]def extract_flake8_requirements() -> Dict[str, str]: return extract_default(name='flake8_requirements.checker')
[docs]def extract_flake8_expression_complexity() -> Dict[str, str]: return extract_default(name='flake8_expression_complexity.checker')
[docs]def extract_flake8_use_fstring() -> Dict[str, str]: codes = dict() codes.update(extract_default(name='flake8_use_fstring.format')) codes.update(extract_default(name='flake8_use_fstring.percent')) # https://github.com/MichaelKim0407/flake8-use-fstring/pull/2 try: codes.update(extract_default(name='flake8_use_fstring.prefix')) except ImportError: pass return codes
[docs]def extract_flake8_functions() -> Dict[str, str]: codes = dict() codes.update(extract_default('flake8_functions.checker')) try: codes.update(extract_default('flake8_functions.function_arguments_amount')) codes.update(extract_default('flake8_functions.function_lenght')) codes.update(extract_default('flake8_functions.function_purity')) except ImportError: pass return codes
# -- HARDCODED CODES -- #
[docs]def extract_flake8_spellcheck() -> Dict[str, str]: return { 'SC100': 'Spelling error in comments', 'SC200': 'Spelling error in name', }
[docs]def extract_flake8_import_order() -> Dict[str, str]: return { 'I666': 'Import statement mixes groups.', 'I100': 'Import statements are in the wrong order.', 'I101': 'Imported names are in the wrong order.', 'I201': 'Missing newline between import groups.', 'I202': 'Additional newline in a group of imports.', }
[docs]def extract_flake8_black() -> Dict[str, str]: # external from flake8_black import black_prefix return { black_prefix + '901': 'Invalid input', black_prefix + '997': 'Invalid TOML file', black_prefix + '999': 'Unexpected exception', }
[docs]def extract_flake8_alfred() -> Dict[str, str]: return {'B1': 'banned symbol'}
[docs]def extract_flake8_eradicate() -> Dict[str, str]: return {'E800': 'Found commented out code: {0}'}
# -- AD-HOC EXTRACTORS -- #
[docs]def extract_flake8_commas() -> Dict[str, str]: # external from flake8_commas._base import ERRORS return dict(ERRORS.values())
[docs]def extract_flake8_debugger() -> Dict[str, str]: # external from flake8_debugger import DEBUGGER_ERROR_CODE return {DEBUGGER_ERROR_CODE: 'trace found'}
[docs]def extract_flake8_mutable() -> Dict[str, str]: # external from mutable_defaults import MutableDefaultChecker return {MutableDefaultChecker._code: MutableDefaultChecker._error_tmpl}
[docs]def extract_flake8_fixme() -> Dict[str, str]: # external from flake8_fixme import WORD_CODES return {code: 'fixme found ({})'.format(word) for word, code in WORD_CODES.items()}
[docs]def extract_pep8_naming() -> Dict[str, str]: # external import pep8ext_naming codes = dict() for checker_name in dir(pep8ext_naming): if not checker_name.endswith('Check'): continue checker = getattr(pep8ext_naming, checker_name) for code, message in checker.__dict__.items(): if code[0] == 'N': codes[code] = message return codes
[docs]def extract_flake8_pyi() -> Dict[str, str]: # external import pyi codes = dict() for name, value in vars(pyi).items(): if name.startswith('Y0'): codes[name] = value return codes
[docs]def extract_flake8_pytest_style() -> Dict[str, str]: # external from flake8_pytest_style import errors codes = dict() for error in vars(errors).values(): if error is errors.Error: continue if not isinstance(error, type): continue if not issubclass(error, errors.Error): continue codes[error.code] = error.message # type: ignore return codes
[docs]def extract_flake8_annotations_complexity() -> Dict[str, str]: _error_message_templates = ['TAE002 too complex annotation ({0} > {1})', 'TAE003 too long annotation ({0} > {1})'] codes = dict() for _error_message_template in _error_message_templates: code, message = _error_message_template.split(' ', maxsplit=1) codes[code] = message return codes
[docs]def extract_flake8_future_import() -> Dict[str, str]: # external from flake8_future_import import ALL_FEATURES codes = dict() tmpl = 'FI{}' for feature in ALL_FEATURES: code = tmpl.format(10 + feature.index) codes[code] = '__future__ import "{}" missing'.format(feature.name) code = tmpl.format(50 + feature.index) codes[code] = '__future__ import "{}" present'.format(feature.name) codes[tmpl.format(90)] = '__future__ import does not exist' return codes
[docs]def extract_flake8_string_format() -> Dict[str, str]: # external from flake8_string_format import StringFormatChecker return {'P{}'.format(c): m for c, m in StringFormatChecker.ERRORS.items()}
[docs]def extract_flake8_bandit() -> Dict[str, str]: # external from bandit.core.extension_loader import MANAGER codes = dict() for blacklist in MANAGER.blacklist.values(): for check in blacklist: code = check['id'].replace('B', 'S') codes[code] = check['message'] for plugin in MANAGER.plugins: code = plugin.plugin._test_id.replace('B', 'S') codes[code] = plugin.name.replace('_', ' ') return codes
[docs]def extract_pylint() -> Dict[str, str]: # external import pylint.checkers try: # external from pylint.lint import MSGS except ImportError: # external from pylint.lint.pylinter import MSGS codes = dict() for code, (msg, alias, *_) in MSGS.items(): if msg in ('%s', '%s: %s'): msg = alias.replace('-', ' ') codes[code] = msg.replace('\n', ' ') for path in Path(pylint.checkers.__path__[0]).iterdir(): module = import_module('pylint.checkers.' + path.stem) for class_name in dir(module): cls = getattr(module, class_name, None) msgs = getattr(cls, 'msgs', None) if not msgs: continue for code, (msg, alias, *_) in msgs.items(): if msg in ('%s', '%s: %s'): msg = alias.replace('-', ' ') codes[code] = msg.replace('\n', ' ') return codes
[docs]def extract_pyflakes() -> Dict[str, str]: # external from flake8.plugins.pyflakes import FLAKE8_PYFLAKES_CODES from pyflakes import messages codes = dict() for class_name, code in FLAKE8_PYFLAKES_CODES.items(): codes[code] = getattr(messages, class_name).message return codes
[docs]def extract_flake8_rst_docstrings() -> Dict[str, str]: # external from flake8_rst_docstrings import code_mappings_by_level codes = dict() for level, codes_mapping in code_mappings_by_level.items(): for message, number in codes_mapping.items(): code = 'RST{}{:02d}'.format(level, number) codes[code] = message return codes
[docs]def extract_flake8_django() -> Dict[str, str]: # external import flake8_django.checkers codes = dict() for path in Path(flake8_django.checkers.__path__[0]).iterdir(): module = import_module('flake8_django.checkers.' + path.stem) for class_name in dir(module): cls = getattr(module, class_name) if not hasattr(cls, 'code'): continue if '0' not in cls.__name__: continue codes[cls.__name__] = cls.description return codes
[docs]def extract_flake8_scrapy() -> Dict[str, str]: # external from flake8_scrapy import ScrapyStyleIssueFinder codes = dict() for finders in ScrapyStyleIssueFinder().finders.values(): for finder in finders: codes[finder.msg_code] = finder.msg_info return codes
[docs]def extract_flake8_executable() -> Dict[str, str]: # external import flake8_executable path = Path(flake8_executable.__file__) content = path.read_text() codes = dict() for code, msg in re.findall(r"'(EXE00\d)', '(.*)'", content): codes[code] = msg return codes
[docs]def extract_flake8_docstrings() -> Dict[str, str]: # external from pydocstyle.violations import ErrorRegistry codes = dict() for group in ErrorRegistry.groups: for error in group.errors: codes[error.code] = error.short_desc return codes
[docs]def extract_dlint() -> Dict[str, str]: # external from dlint.linters import ALL codes = dict() for linter in ALL: code, msg = linter._error_tmpl.split(' ', maxsplit=1) codes[code] = msg return codes
[docs]def extract_flake8_mock() -> Dict[str, str]: # external from flake8_mock import ERROR_MESSAGE, MOCK_ERROR_CODE message = ERROR_MESSAGE.split(' ', maxsplit=1)[1] return {MOCK_ERROR_CODE: message}
[docs]def extract_flake8_pytest() -> Dict[str, str]: # external from flake8_pytest import PYTEST_ERROR_CODE, PYTEST_ERROR_MESSAGE return {PYTEST_ERROR_CODE: PYTEST_ERROR_MESSAGE}
[docs]def extract_wemake_python_styleguide() -> Dict[str, str]: # external from wemake_python_styleguide import violations codes = dict() for path in Path(violations.__path__[0]).iterdir(): module = import_module('wemake_python_styleguide.violations.' + path.stem) for checker_name in dir(module): if not checker_name.endswith('Violation'): continue checker = getattr(module, checker_name) if not hasattr(checker, 'code'): continue code = 'WPS' + str(checker.code).zfill(3) codes[code] = checker.error_template return codes
[docs]def extract_flake8_pie() -> Dict[str, str]: # built-in from inspect import getsource # external import flake8_pie codes = dict() for path in Path(flake8_pie.__path__[0]).iterdir(): module = import_module('flake8_pie.' + path.stem) for code in dir(module): if not code.startswith('PIE'): continue error = getattr(module, code) if isinstance(error, partial): code_message = error.keywords['message'] else: fn_source = getsource(error) code_message = fn_source.split('message=')[1].strip('\n (),\'"') codes[code] = code_message.split(': ', maxsplit=1)[1] return codes