Source code for flakeheaven._logic._plugin

# built-in
import re
from pathlib import Path
from typing import Any, Dict, List, Tuple, Union

# external
from flake8.utils import fnmatch


REX_NAME = re.compile(r'[-_.]+')
ALIASES = {
    'aaa': 'flake8-aaa',
    'flake-mutable': 'flake8-mutable',
    'flake8-pandas-vet': 'pandas-vet',
    'import-order': 'flake8-import-order',
    'logging-format': 'flake8-logging-format',
    'naming': 'pep8-naming',
    'pyflakes': 'pyflakes',
    'sql': 'flake8-sql',
    'use-fstring-format': 'flake8-use-fstring',
    'use-fstring-percent': 'flake8-use-fstring',
    'use-fstring-prefix': 'flake8-use-fstring',
}
PluginsType = Dict[str, List[str]]


[docs]def get_plugin_name(plugin: Dict[str, Any]) -> str: """Get plugin name from plugin info Users expect the same plugin name as the name of the package that provides plugin. However, that's not true for some plugins. Also, some plugins has different module name, that doesn't match to package. Lookup order: 1. Ad-hoc aliases when nothing match 2. Normalized name that starts with `flake8` 3. Normalized name that starts with `pep` 4. `plugin_name` """ if not plugin: return 'UNKNOWN' if plugin['plugin_name'] in ALIASES: return ALIASES[plugin['plugin_name']] names = (plugin['plugin_name'], plugin['plugin'].__module__) names = [REX_NAME.sub('-', name).lower() for name in names] for name in names: if name.startswith('flake8'): return name for name in names: if name.startswith('pep8'): return name return names[0]
[docs]def get_plugin_rules(plugin_name: str, plugins: PluginsType) -> List[str]: """Get rules for plugin from `plugins` in the config Plugin name can be specified as a glob expression. So, it's not trivial to match the right one 1. Try to find exact match (normalizing ass all packages names normalized) 2. Try to find globs that match and select the longest one (nginx-style) 3. Return empty list if nothing is found. """ if not plugins: return [] plugin_name = REX_NAME.sub('-', plugin_name).lower() # try to find exact match for pattern, rules in plugins.items(): if '*' not in pattern and REX_NAME.sub('-', pattern).lower() == plugin_name: return rules # try to find match by pattern and select the longest best_match = (0, []) # type: Tuple[int, List[str]] for pattern, rules in plugins.items(): if not fnmatch(filename=plugin_name, patterns=[pattern]): continue match = len(pattern) if match > best_match[0]: best_match = match, rules if best_match[0]: return best_match[1] return []
[docs]def check_include(code: str, rules: List[str]) -> bool: """ 0. Validate rules 1. Return True if rule explicitly included 2. Return False if rule explicitly excluded 3. Return True if the latest glob-matching rule is include 4. Return False if the latest glob-matching rule is exclude """ # always report exceptions in file processing if code in ('E902', 'E999'): return True for rule in rules: if len(rule) < 2 or rule[0] not in {'-', '+'}: raise ValueError('invalid rule: `{}`'.format(rule)) for rule in reversed(rules): if code.lower() == rule[1:].lower(): return rule[0] == '+' include = False for rule in rules: if fnmatch(code, patterns=[rule[1:]]): include = rule[0] == '+' return include
[docs]def get_exceptions( path: Union[str, Path], exceptions: Dict[str, PluginsType], root: Path = None, ) -> PluginsType: """ Just like in get_plugin_rules. The algorithm: 1. Try to find exact match (normalizing as all packages names normalized) 2. Try to find globs that match and select the longest one (nginx-style) """ if not exceptions: return dict() if isinstance(path, str): path = Path(path) if root is None: root = Path().resolve() try: path = path.resolve().relative_to(root).as_posix() except ValueError: # path is not in the project root return dict() exceptions = sorted( exceptions.items(), key=lambda item: len(item[0]), ) aggregated_rules = dict() # glob for path_rule, rules in exceptions: if '*' not in path_rule: continue if fnmatch(filename=path, patterns=[path_rule]): aggregated_rules.update(rules) # prefix for path_rule, rules in exceptions: if '*' in path_rule: continue if path.startswith(path_rule): aggregated_rules.update(rules) return aggregated_rules