PK!uh$wemake_python_styleguide/__init__.py# -*- coding: utf-8 -*- PK!X&#wemake_python_styleguide/checker.py# -*- coding: utf-8 -*- """ Entry point to the app. Writing new plugin ------------------ First of all, you have to decide: 1. Are you writing a separate plugin and adding it as a dependency? 2. Are you writing an built-in extension to this styleguide? How to make a decision? Will this plugin be useful to other developers without this styleguide? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If so, it would be wise to create a separate ``flake8`` plugin. Then you can add newly created plugin as a dependency. Our rules do not make any sense without each other. Real world examples: - `flake8-eradicate `_ Can this plugin be used with the existing checker? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``flake8`` has a very strict API about plugins. Here are some problems that you may encounter: - Some plugins are called once per file, some are called once per line - Plugins should define clear ``violation code`` / ``checker`` relation - It is impossible to use the same letter violation codes for several checkers Real world examples: - `flake8-broken-line `_ Writing new visitor ------------------- If you are still willing to write a builtin extension to our styleguide, you will have to write a :ref:`violation ` and/or :ref:`visitor `. Checker API ----------- .. autoclass:: Checker :members: :special-members: :exclude-members: __weakref__ """ import ast import tokenize from typing import ClassVar, Generator, Sequence, Type from flake8.options.manager import OptionManager from wemake_python_styleguide import constants, types from wemake_python_styleguide import version as pkg_version from wemake_python_styleguide.options.config import Configuration from wemake_python_styleguide.visitors import base from wemake_python_styleguide.visitors.presets import ( complexity, general, tokens, ) VisitorClass = Type[base.BaseVisitor] @types.final class Checker(object): """ Main checker class. It is an entry point to the whole app. Attributes: name: required by the ``flake8`` API, should match the package name. version: required by the ``flake8`` API, defined in the packaging file. config: custom configuration object used to provide and parse options. options: option structure passed by ``flake8``. visitors: sequence of visitors that we run with this checker. """ name: ClassVar[str] = pkg_version.pkg_name version: ClassVar[str] = pkg_version.pkg_version config = Configuration() options: types.ConfigurationOptions visitors: ClassVar[Sequence[VisitorClass]] = ( *general.GENERAL_PRESET, *complexity.COMPLEXITY_PRESET, *tokens.TOKENS_PRESET, ) def __init__( self, tree: ast.AST, file_tokens: Sequence[tokenize.TokenInfo], filename: str = constants.STDIN, ) -> None: """ Creates new checker instance. These parameter names should not be changed. ``flake8`` has special API that passes concrete parameters to the plugins that ask for them. ``flake8`` also decides how to execute this plugin based on its parameters. This one is executed once per module. Parameters: tree: ``ast`` parsed by ``flake8``. Differs from ``ast.parse``. file_tokens: ``tokenize.tokenize`` parsed file tokens. filename: module file name, might be empty if piping is used. See also: http://flake8.pycqa.org/en/latest/plugin-development/index.html """ self.tree = tree self.filename = filename self.file_tokens = file_tokens @classmethod def add_options(cls, parser: OptionManager) -> None: """ ``flake8`` api method to register new plugin options. See :class:`.Configuration` docs for detailed options reference. Arguments: parser: ``flake8`` option parser instance. """ cls.config.register_options(parser) @classmethod def parse_options(cls, options: types.ConfigurationOptions) -> None: """Parses registered options for providing them to each visitor.""" cls.options = options def _run_checks( self, visitors: Sequence[VisitorClass], ) -> Generator[types.CheckResult, None, None]: """ Runs all passed visitors one by one. Yields: Violations that were found by the passed visitors. """ for visitor_class in visitors: visitor = visitor_class.from_checker(self) visitor.run() for error in visitor.violations: yield (*error.node_items(), type(self)) def run(self) -> Generator[types.CheckResult, None, None]: """ Runs the checker. This method is used by ``flake8`` API. It is executed after all configuration is parsed. """ yield from self._run_checks(self.visitors) PK!p+ + %wemake_python_styleguide/constants.py# -*- coding: utf-8 -*- """ This module contains list of white- and black-listed ``python`` members. It contains lists of keywords and built-in functions we discourage to use. It also contains some exceptions that we allow to use in our codebase. """ import re from wemake_python_styleguide.types import Final #: List of functions we forbid to use. FUNCTIONS_BLACKLIST: Final = frozenset(( # Code generation: 'eval', 'exec', 'compile', # Termination: 'exit', 'quit', # Magic: 'globals', 'locals', 'vars', 'dir', # IO: 'input', # Attribute access: 'hasattr', 'delattr', # Gratis: 'copyright', 'help', 'credits', # Dynamic imports: '__import__', # OOP: 'staticmethod', )) #: List of module metadata we forbid to use. MODULE_METADATA_VARIABLES_BLACKLIST: Final = frozenset(( '__author__', '__all__', '__version__', '__about__', )) #: List of variable names we forbid to use. VARIABLE_NAMES_BLACKLIST: Final = frozenset(( # Meaningless words: 'data', 'result', 'results', 'item', 'items', 'value', 'values', 'val', 'vals', 'var', 'vars', 'content', 'contents', 'info', 'handle', 'handler', 'file', 'obj', 'objects', 'objs', 'some', # Confuseables: 'no', 'true', 'false', # Names from examples: 'foo', 'bar', 'baz', )) #: List of magic methods that are forbidden to use. MAGIC_METHODS_BLACKLIST: Final = frozenset(( # Since we don't use `del`: '__del__', '__delitem__', '__delete__', '__dir__', # since we don't use `dir()` '__delattr__', # since we don't use `delattr()` )) #: List of nested classes' names we allow to use. NESTED_CLASSES_WHITELIST: Final = frozenset(( 'Meta', # django forms, models, drf, etc 'Params', # factoryboy specific )) #: List of nested functions' names we allow to use. NESTED_FUNCTIONS_WHITELIST: Final = frozenset(( 'decorator', 'factory', )) #: List of allowed ``__future__`` imports. FUTURE_IMPORTS_WHITELIST: Final = frozenset(( 'annotations', 'generator_stop', )) #: List of blacklisted module names. MODULE_NAMES_BLACKLIST: Final = frozenset(( 'util', 'utils', 'utilities', 'helpers', )) #: List of allowed module magic names. MAGIC_MODULE_NAMES_WHITELIST: Final = frozenset(( '__init__', '__main__', )) #: Regex pattern to name modules. MODULE_NAME_PATTERN: Final = re.compile(r'^_?_?[a-z][a-z\d_]+[a-z\d](__)?$') #: Common numbers that are allowed to be used without being called "magic". MAGIC_NUMBERS_WHITELIST: Final = frozenset(( 0.5, 100, 1000, 1024, # bytes 24, # hours 60, # seconds, minutes )) # Internal variables # They are not publicly documented since they are not used by the end user. # Used as a default filename, when it is not passed by flake8: STDIN: Final = 'stdin' # Used as a special name for unused variables: UNUSED_VARIABLE: Final = '_' # Used to specify as a placeholder for `__init__`: INIT: Final = '__init__' # Allowed magic number modulo: NON_MAGIC_MODULO: Final = 10 # Used to specify a pattern which checks variables and modules for underscored # numbers in their names: UNDERSCORED_NUMBER_PATTERN: Final = re.compile(r'.*\D_\d(\D|$)') PK!uh+wemake_python_styleguide/logics/__init__.py# -*- coding: utf-8 -*- PK![ ,wemake_python_styleguide/logics/filenames.py# -*- coding: utf-8 -*- from pathlib import PurePath from typing import Iterable from wemake_python_styleguide import constants from wemake_python_styleguide.options import defaults def _get_stem(file_path: str) -> str: return PurePath(file_path).stem def is_stem_in_list(file_path: str, to_check: Iterable[str]) -> bool: """ Checks whether module's name is included in a search list. >>> is_stem_in_list('/some/module.py', ['other']) False >>> is_stem_in_list('partial/module.py', ['module']) True >>> is_stem_in_list('module.py', ['module']) True >>> is_stem_in_list('C:/User/package/__init__.py', ['__init__']) True """ return _get_stem(file_path) in to_check def is_magic(file_path: str) -> bool: """ Checks whether the given `file_path` contains the magic module name. >>> is_magic('__init__.py') True >>> is_magic('some.py') False >>> is_magic('/home/user/cli.py') False >>> is_magic('/home/user/__version__.py') True >>> is_magic('D:/python/__main__.py') True """ stem = _get_stem(file_path) return stem.startswith('__') and stem.endswith('__') def is_too_short_stem( file_path: str, min_length: int = defaults.MIN_MODULE_NAME_LENGTH, ) -> bool: """ Checks whether the file's stem fits into the minimum length. >>> is_too_short_stem('a.py') True >>> is_too_short_stem('prefix/b.py') True >>> is_too_short_stem('regular.py') False >>> is_too_short_stem('c:/package/abc.py', min_length=4) True """ stem = _get_stem(file_path) return len(stem) < min_length def is_matching_pattern(file_path: str) -> bool: """ Checks whether the file's stem matches the given pattern. >>> is_matching_pattern('some.py') True >>> is_matching_pattern('__init__.py') True >>> is_matching_pattern('MyModule.py') False """ stem = _get_stem(file_path) return constants.MODULE_NAME_PATTERN.match(stem) is not None def is_stem_with_underscored_number(file_path: str) -> bool: """ Checks whether the file's stem contains an underscored number. >>> is_stem_with_underscored_number('episode2.py') True >>> is_stem_with_underscored_number('episode_2.py') False >>> is_stem_with_underscored_number('come2_me.py') True """ stem = _get_stem(file_path) return constants.UNDERSCORED_NUMBER_PATTERN.match(stem) is None PK!s,wemake_python_styleguide/logics/functions.py# -*- coding: utf-8 -*- from ast import Call from typing import Iterable, Optional def given_function_called(node: Call, to_check: Iterable[str]) -> str: """ Returns function name if it is called and contained in the `to_check`. >>> import ast >>> module = ast.parse('print("some value")') >>> given_function_called(module.body[0].value, ['print']) 'print' """ function_name = getattr(node.func, 'id', None) function_value = getattr(node.func, 'value', None) function_inner_id = getattr(function_value, 'id', None) function_attr = getattr(node.func, 'attr', None) is_restricted_function_attribute = ( function_inner_id in to_check and function_attr in to_check ) if function_name in to_check or is_restricted_function_attribute: return function_name return '' def is_method(function_type: Optional[str]) -> bool: """ Returns whether a given function type belongs to a class. >>> is_method('function') False >>> is_method(None) False >>> is_method('method') True >>> is_method('classmethod') True >>> is_method('') False """ return function_type in ['method', 'classmethod'] PK!7N@@*wemake_python_styleguide/logics/imports.py# -*- coding: utf-8 -*- import ast from typing import List from wemake_python_styleguide.types import AnyImport def get_error_text(node: AnyImport) -> str: """Returns correct error text for import nodes.""" module = getattr(node, 'module', None) if module is not None: return module if isinstance(node, ast.Import): return node.names[0].name return '.' def get_import_parts(node: AnyImport) -> List[str]: """Returns list of import modules.""" module_path = getattr(node, 'module', '') or '' return module_path.split('.') PK!ע(wemake_python_styleguide/logics/nodes.py# -*- coding: utf-8 -*- import ast from typing import Tuple, Type def is_literal(node: ast.AST) -> bool: """ Checks for nodes that contains only constants. If the node contains only literals it will be evaluated. When node relies on some other names, it won't be evaluated. """ try: ast.literal_eval(node) except ValueError: return False else: return True def is_contained(node: ast.AST, to_check: Tuple[Type[ast.AST], ...]) -> bool: """Checks whether node does contain given subnode types.""" for child in ast.walk(node): if isinstance(child, to_check): return True return False PK!uh5wemake_python_styleguide/logics/variables/__init__.py# -*- coding: utf-8 -*- PK!(l""3wemake_python_styleguide/logics/variables/access.py# -*- coding: utf-8 -*- def is_private_variable(name: str) -> bool: """ Checks if variable has private name pattern. >>> is_private_variable('regular') False >>> is_private_variable('__private') True >>> is_private_variable('_protected') False >>> is_private_variable('__magic__') False """ return name.startswith('__') and not name.endswith('__') def is_protected_variable(name: str) -> bool: """ Checks if variable has protected name pattern. >>> is_protected_variable('_protected') True >>> is_protected_variable('__private') False >>> is_protected_variable('__magic__') False >>> is_protected_variable('common_variable') False """ return name.startswith('_') and not name.startswith('__') PK![x7wemake_python_styleguide/logics/variables/name_nodes.py# -*- coding: utf-8 -*- import ast from typing import Optional def is_same_variable(left: ast.AST, right: ast.AST) -> bool: """Ensures that nodes are the same variable.""" if isinstance(left, ast.Name) and isinstance(right, ast.Name): return left.id == right.id return False def get_assigned_name(node: ast.AST) -> Optional[str]: """ Returns variable names for node that is just assigned. Returns ``None`` for nodes that are used in a different manner. """ if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Store): return node.id if isinstance(node, ast.Attribute) and isinstance(node.ctx, ast.Store): return node.attr if isinstance(node, ast.ExceptHandler): return getattr(node, 'name', None) return None PK!MW 3wemake_python_styleguide/logics/variables/naming.py# -*- coding: utf-8 -*- from typing import Iterable from wemake_python_styleguide import constants from wemake_python_styleguide.options import defaults def is_wrong_variable_name(name: str, to_check: Iterable[str]) -> bool: """ Checks that variable is not prohibited by explicitly listing it's name. >>> is_wrong_variable_name('wrong', ['wrong']) True >>> is_wrong_variable_name('correct', ['wrong']) False >>> is_wrong_variable_name('_wrong', ['wrong']) True >>> is_wrong_variable_name('wrong_', ['wrong']) True >>> is_wrong_variable_name('wrong__', ['wrong']) False >>> is_wrong_variable_name('__wrong', ['wrong']) False """ for name_to_check in to_check: choices_to_check = [ name_to_check, '_{0}'.format(name_to_check), '{0}_'.format(name_to_check), ] if name in choices_to_check: return True return False def is_upper_case_name(name: str) -> bool: """ Checks that attribute name has no upper-case letters. >>> is_upper_case_name('camelCase') True >>> is_upper_case_name('UPPER_CASE') True >>> is_upper_case_name('camel_Case') True >>> is_upper_case_name('snake_case') False >>> is_upper_case_name('snake') False >>> is_upper_case_name('snake111') False >>> is_upper_case_name('__variable_v2') False """ return any(character.isupper() for character in name) def is_too_short_variable_name( name: str, min_length: int = defaults.MIN_VARIABLE_LENGTH, ) -> bool: """ Checks for too short variable names. >>> is_too_short_variable_name('test') False >>> is_too_short_variable_name('o') True >>> is_too_short_variable_name('_') False >>> is_too_short_variable_name('z1') False >>> is_too_short_variable_name('z', min_length=1) False """ return name != constants.UNUSED_VARIABLE and len(name) < min_length def is_variable_name_with_underscored_number(name: str) -> bool: """ Checks for variable names with underscored number. >>> is_variable_name_with_underscored_number('star_wars_episode2') False >>> is_variable_name_with_underscored_number('come2_me') False >>> is_variable_name_with_underscored_number('_') False >>> is_variable_name_with_underscored_number('z1') False >>> is_variable_name_with_underscored_number('star_wars_episode_2') True >>> is_variable_name_with_underscored_number('come_2_me') True >>> is_variable_name_with_underscored_number('iso_123_456') False """ pattern = constants.UNDERSCORED_NUMBER_PATTERN return pattern.match(name) is not None def is_variable_name_contains_consecutive_underscores(name: str) -> bool: """ Checks if variable contains consecutive underscores in middle of name. >>> is_variable_name_contains_consecutive_underscores('name') False >>> is_variable_name_contains_consecutive_underscores('__magic__') False >>> is_variable_name_contains_consecutive_underscores('__private') False >>> is_variable_name_contains_consecutive_underscores('name') False >>> is_variable_name_contains_consecutive_underscores('some__value') True >>> is_variable_name_contains_consecutive_underscores('some_value__') True """ if name.startswith('__'): return False if '__' in name: return True return False PK!uh,wemake_python_styleguide/options/__init__.py# -*- coding: utf-8 -*- PK!)@*wemake_python_styleguide/options/config.py# -*- coding: utf-8 -*- from typing import ClassVar, Dict, Optional, Sequence, Union import attr from flake8.options.manager import OptionManager from wemake_python_styleguide.options import defaults from wemake_python_styleguide.types import final ConfigValues = Dict[str, Union[str, int, bool]] @final @attr.attrs(frozen=True, auto_attribs=True, slots=True) class _Option(object): """Represents ``flake8`` option object.""" long_option_name: str default: int # noqa: E704 help: str type: Optional[str] = 'int' # noqa: A003 parse_from_config: bool = True action: str = 'store' @final class Configuration(object): """ Provides configuration options for our plugin. We do not like our linter to be configurable. Since people may take the wrong path or make wrong decisions. We try to make all defaults as reasonable as possible. However, you can currently adjust some complexity options. Why? Because we are not quite sure about the ideal values yet. We are still researching them, and providing a way for developers to help us out is a good thing at the moment. Options for general checks: - ``min-variable-length`` - minimum number of chars to define a valid variable name, defaults to :str:`wemake_python_styleguide.options.defaults.MIN_VARIABLE_LENGTH` - ``i-control-code`` - whether you control ones who use your code, more rules are enforced when you do control it, defaults to :str:`wemake_python_styleguide.options.defaults.I_CONTROL_CODE` Options for module names related checks: - ``min-module-name-length`` - minimum required module's name length, defaults to :str:`wemake_python_styleguide.options.defaults.MIN_MODULE_NAME_LENGTH` Options for complexity related checks: - ``max-returns`` - maximum allowed number of ``return`` statements in one function, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_RETURNS` - ``max-local-variables`` - maximum allowed number of local variables in one function, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_LOCAL_VARIABLES` - ``max-expressions`` - maximum allowed number of expressions in one function, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_EXPRESSIONS` - ``max-arguments`` - maximum allowed number of arguments in one function, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_ARGUMENTS` - ``max-offset-blocks`` - maximum number of block to nest expressions, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_OFFSET_BLOCKS` - ``max-elifs`` - maximum number of ``elif`` blocks, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_ELIFS` - ``max-module-members`` - maximum number of classes and functions in a single module, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_MODULE_MEMBERS` - ``max-methods`` - maximum number of methods in a single class, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_METHODS` - ``max-line-complexity`` - maximum line complexity measured in number of ``ast`` nodes per line, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_LINE_COMPLEXITY` - ``max-jones-score`` - maximum Jones score for a module, which is equal to the median of all lines complexity sum, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_JONES_SCORE` - ``max-imports`` - maximum number of imports in a single module, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_IMPORTS` - ``max-conditions`` - maximum number of boolean conditions in a single ``if`` or ``while`` node, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_CONDITIONS` - ``max-base-classes`` - maximum number of parent classes inside a class definition, defaults to :str:`wemake_python_styleguide.options.defaults.MAX_BASE_CLASSES` All options are configurable via ``flake8`` CLI: Example:: flake8 --max-returns=2 --max-elifs=2 Or you can provide options in ``tox.ini`` or ``setup.cfg``: Example:: [flake8] max-returns = 2 max-elifs = 2 We use ``setup.cfg`` as a default way to provide configuration. """ options: ClassVar[Sequence[_Option]] = [ # Complexity: _Option( '--max-returns', defaults.MAX_RETURNS, 'Maximum allowed number of return statements in one function.', ), _Option( '--max-local-variables', defaults.MAX_LOCAL_VARIABLES, 'Maximum allowed number of local variables in one function.', ), _Option( '--max-expressions', defaults.MAX_EXPRESSIONS, 'Maximum allowed number of expressions in one function.', ), _Option( '--max-arguments', defaults.MAX_ARGUMENTS, 'Maximum allowed number of arguments in one function.', ), _Option( '--max-offset-blocks', defaults.MAX_OFFSET_BLOCKS, 'Maximum number of blocks to nest different structures.', ), _Option( '--max-elifs', defaults.MAX_ELIFS, 'Maximum number of `elif` blocks.', ), _Option( '--max-module-members', defaults.MAX_MODULE_MEMBERS, 'Maximum number of classes and functions in a single module.', ), _Option( '--max-methods', defaults.MAX_METHODS, 'Maximum number of methods in a single class.', ), _Option( '--max-line-complexity', defaults.MAX_LINE_COMPLEXITY, 'Maximum line complexity, measured in `ast` nodes.', ), _Option( '--max-jones-score', defaults.MAX_JONES_SCORE, 'Maximum median module complexity, based on sum of lines.', ), _Option( '--max-imports', defaults.MAX_IMPORTS, 'Maximum number of imports in a single module.', ), _Option( '--max-conditions', defaults.MAX_CONDITIONS, 'Maximum number of conditions in a `if` or `while` node.', ), _Option( '--max-base-classes', defaults.MAX_BASE_CLASSES, 'Maximum number of base classes', ), # General: _Option( '--min-variable-length', defaults.MIN_VARIABLE_LENGTH, 'Minimum required length of the variable name.', ), _Option( '--i-control-code', defaults.I_CONTROL_CODE, 'Whether you control ones who use your code.', action='store_true', type=None, ), # File names: _Option( '--min-module-name-length', defaults.MIN_MODULE_NAME_LENGTH, "Minimum required module's name length", ), ] def register_options(self, parser: OptionManager) -> None: """Registers options for our plugin.""" for option in self.options: parser.add_option(**attr.asdict(option)) PK!fT ,wemake_python_styleguide/options/defaults.py# -*- coding: utf-8 -*- """ Constants with default values for plugin's configuration. We try to stick to "the magical 7 ± 2 number". https://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two What does it mean? It means that we choose these values based on our mind capacity. And it is really hard to keep in mind more that 9 objects at the same time. These values can be changed in the ``setup.cfg`` file on a per-project bases, if you find them too strict or too permissive. """ from wemake_python_styleguide.types import Final # General #: Minimum variable's name length: MIN_VARIABLE_LENGTH: Final = 2 #: Whether you control ones who use your code: I_CONTROL_CODE: Final = True # Complexity #: Maximum number of `return` statements allowed in a single function: MAX_RETURNS: Final = 5 #: Maximum number of local variables in a function: MAX_LOCAL_VARIABLES: Final = 5 #: Maximum number of expressions in a single function: MAX_EXPRESSIONS: Final = 9 #: Maximum number of arguments for functions or method, `self` is not counted: MAX_ARGUMENTS: Final = 5 #: Maximum number of blocks to nest different structures: MAX_OFFSET_BLOCKS: Final = 5 #: Maximum number of `elif` blocks in a single `if` condition: MAX_ELIFS: Final = 3 #: Maximum number of classes and functions in a single module: MAX_MODULE_MEMBERS: Final = 7 #: Maximum number of methods in a single class: MAX_METHODS: Final = 7 #: Maximum line complexity: MAX_LINE_COMPLEXITY: Final = 14 # 7 * 2, also almost guessed #: Maximum median module Jones complexity: MAX_JONES_SCORE: Final = 12 # this value was "guessed" #: Maximum number of imports in a single module: MAX_IMPORTS: Final = 12 #: Maximum number of conditions in a single ``if`` or ``while`` statement: MAX_CONDITIONS: Final = 4 #: Maximum number of base classes: MAX_BASE_CLASSES: Final = 3 # Modules #: Minimum required module's name length: MIN_MODULE_NAME_LENGTH: Final = 3 PK!upp!wemake_python_styleguide/types.py# -*- coding: utf-8 -*- """ This module contains custom ``mypy`` types that we commonly use. Policy ------ If any of the following statements is true, move the type to this file: - if type is used in multiple files - if type is complex enough it has to be documented - if type is very important for the public API """ import ast from typing import Tuple, Type, Union from typing_extensions import Final, Protocol, final # noqa: F401 #: In cases we need to work with both import types. AnyImport = Union[ast.Import, ast.ImportFrom] #: In cases we need to work with both function definitions. AnyFunctionDef = Union[ast.FunctionDef, ast.AsyncFunctionDef] #: In cases we need to work with all function definitions (including lambdas). AnyFunctionDefAndLambda = Union[AnyFunctionDef, ast.Lambda] #: In cases we need to work with both forms of if functions AnyIf = Union[ast.If, ast.IfExp] #: Flake8 API format to return error messages: CheckResult = Tuple[int, int, str, type] #: Tuple of AST node types for declarative syntax. AnyNodes = Tuple[Type[ast.AST], ...] class ConfigurationOptions(Protocol): """ Provides structure for the options we use in our checker and visitors. Then this protocol is passed to each individual visitor. It uses structural sub-typing, and does not represent any kind of a real class or structure. See also: https://mypy.readthedocs.io/en/latest/protocols.html """ # General: min_variable_length: int i_control_code: bool # Complexity: max_arguments: int max_local_variables: int max_returns: int max_expressions: int max_offset_blocks: int max_elifs: int max_module_members: int max_methods: int max_line_complexity: int max_jones_score: int max_imports: int max_conditions: int max_base_classes: int # File names: min_module_name_length: int PK!.m#wemake_python_styleguide/version.py# -*- coding: utf-8 -*- import pkg_resources def _get_version(dist_name: str) -> str: # pragma: no cover """Fetches distribution name. Contains a fix for Sphinx.""" try: return pkg_resources.get_distribution(dist_name).version except pkg_resources.DistributionNotFound: return '' # readthedocs can not install `poetry` projects pkg_name = 'wemake-python-styleguide' #: We store the version number inside the `pyproject.toml`: pkg_version: str = _get_version(pkg_name) PK!Y/wemake_python_styleguide/violations/__init__.py# -*- coding: utf-8 -*- # TODO(@sobolevn): sort all new violations # TODO(@sobolevn): ensure that documentation is also sorted # TODO(@sobolevn): write tests for testing violation sorting order? PK!&)**+wemake_python_styleguide/violations/base.py# -*- coding: utf-8 -*- """ Contains detailed information about violation and how to use them. .. _violations: Writing new violation --------------------- First of all, you have to select the correct base class for new violation. The main criteria is what logic will be used to find the flaw in your code. .. currentmodule:: wemake_python_styleguide.violations.base Available base classes ~~~~~~~~~~~~~~~~~~~~~~ .. autosummary:: :nosignatures: ASTViolation TokenizeViolation SimpleViolation Violation can not have more than one base class. Since it does not make sense to have two different node types at the same time. Violations API -------------- """ import ast import tokenize from typing import ClassVar, Tuple, Union from wemake_python_styleguide.types import final #: General type for all possible nodes where error happens. ErrorNode = Union[ ast.AST, tokenize.TokenInfo, None, ] class BaseViolation(object): """ Abstract base class for all style violations. It basically just defines how to create any error and how to format this error later on. Each subclass must define ``error_template`` and ``code`` fields. Attributes: error_template: message that will be shown to user after formatting. code: violation unique number. Used to identify the violation. should_use_text: formatting option. Some do not require extra text. """ error_template: ClassVar[str] code: ClassVar[int] should_use_text: ClassVar[bool] = True def __init__(self, node: ErrorNode, text: str = None) -> None: """ Creates new instance of abstract violation. Parameters: node: violation was raised by this node. If applied. text: extra text to format the final message. If applied. """ self._node = node if text is None: self._text = node.__class__.__name__.lower() else: self._text = text @final def _full_code(self) -> str: """ Returns fully formatted code. Adds violation letter to the numbers. Also ensures that codes like ``3`` will be represented as ``Z003``. """ return 'Z' + str(self.code).zfill(3) def _location(self) -> Tuple[int, int]: """ Return violation location inside the file. Default location is in the so-called "file beginning". """ return 0, 0 @final def message(self) -> str: """ Returns error's formatted message with code and reason. Conditionally formats the ``error_template`` if it is required. """ if self.should_use_text: message = self.error_template.format(self._text) else: message = self.error_template return '{0} {1}'.format(self._full_code(), message) @final def node_items(self) -> Tuple[int, int, str]: """Returns tuple to match ``flake8`` API format.""" return (*self._location(), self.message()) class ASTViolation(BaseViolation): """Violation for ``ast`` based style visitors.""" _node: ast.AST @final def _location(self) -> Tuple[int, int]: line_number = getattr(self._node, 'lineno', 0) column_offset = getattr(self._node, 'col_offset', 0) return line_number, column_offset class TokenizeViolation(BaseViolation): """Violation for ``tokenize`` based visitors.""" _node: tokenize.TokenInfo @final def _location(self) -> Tuple[int, int]: return self._node.start class SimpleViolation(BaseViolation): """Violation for cases where there's no associated nodes.""" _node: None def __init__(self, node=None, text: str = None) -> None: """Creates new instance of simple style violation.""" super().__init__(node, text=text) PK!SbUU5wemake_python_styleguide/violations/best_practices.py# -*- coding: utf-8 -*- """ These checks ensures that you follow the best practices. The source for these best practices is hidden inside countless hours we have spent debugging software or reviewing it. How do we find an inspiration for new rules? We find some ugly code during code reviews and audits. Then we forbid to use it forever. So, this error will never return to our codebase. .. currentmodule:: wemake_python_styleguide.violations.best_practices Summary ------- .. autosummary:: :nosignatures: WrongMagicCommentViolation WrongDocCommentViolation WrongModuleMetadataViolation EmptyModuleViolation InitModuleHasLogicViolation WrongKeywordViolation WrongFunctionCallViolation FutureImportViolation RaiseNotImplementedViolation BaseExceptionViolation NestedFunctionViolation NestedClassViolation MagicNumberViolation StaticMethodViolation BadMagicMethodViolation NestedImportViolation RedundantForElseViolation RedundantFinallyViolation ReassigningVariableToItselfViolation YieldInsideInitViolation ProtectedModuleViolation ProtectedAttributeViolation Comments -------- .. autoclass:: WrongMagicCommentViolation .. autoclass:: WrongDocCommentViolation Modules ------- .. autoclass:: WrongModuleMetadataViolation .. autoclass:: EmptyModuleViolation .. autoclass:: InitModuleHasLogicViolation Builtins -------- .. autoclass:: WrongKeywordViolation .. autoclass:: WrongFunctionCallViolation .. autoclass:: FutureImportViolation .. autoclass:: RaiseNotImplementedViolation .. autoclass:: BaseExceptionViolation Design ------ .. autoclass:: NestedFunctionViolation .. autoclass:: NestedClassViolation .. autoclass:: MagicNumberViolation .. autoclass:: StaticMethodViolation .. autoclass:: BadMagicMethodViolation .. autoclass:: NestedImportViolation .. autoclass:: RedundantForElseViolation .. autoclass:: RedundantFinallyViolation .. autoclass:: ReassigningVariableToItselfViolation .. autoclass:: YieldInsideInitViolation .. autoclass:: ProtectedModuleViolation .. autoclass:: ProtectedAttributeViolation """ from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.base import ( ASTViolation, SimpleViolation, TokenizeViolation, ) @final class WrongMagicCommentViolation(SimpleViolation): """ Restricts to use several control (or magic) comments. We do not allow to use: 1. ``# noqa`` comment without specified violations 2. ``# type: some_type`` comments to specify a type for ``typed_ast`` Reasoning: We cover several different use-cases in a single rule. ``# noqa`` comment is restricted because it can hide other violations. ``# type: some_type`` comment is restricted because we can already use type annotations instead. Solution: Use ``# noqa`` comments with specified error types. Use type annotations to specify types. We still allow to use ``# type: ignore`` comment. Since sometimes it is totally required. Example:: # Correct: type = MyClass.get_type() # noqa: A001 coordinate: int = 10 some.int_field = 'text' # type: ignore # Wrong: type = MyClass.get_type() # noqa coordinate = 10 # type: int .. versionadded:: 0.1.0 Note: Returns Z400 as error code """ code = 400 #: Error message shown to the user. error_template = 'Found wrong magic comment: {0}' @final class WrongDocCommentViolation(TokenizeViolation): """ Forbids to use empty doc comments (``#:``). Reasoning: Doc comments are used to provide a documentation. But supplying empty doc comments breaks this use-case. It is unclear why they can be used with no contents. Solution: Add some documentation to this comment. Or remove it. Empty doc comments are not caught by the default ``pycodestyle`` checks. Example:: # Correct: #: List of allowed names: NAMES_WHITELIST = ['feature', 'bug', 'research'] # Wrong: #: NAMES_WHITELIST = ['feature', 'bug', 'research'] .. versionadded:: 0.1.0 Note: Returns Z401 as error code """ code = 401 should_use_text = False #: Error message shown to the user. error_template = 'Found wrong doc comment' # Modules: @final class WrongModuleMetadataViolation(ASTViolation): """ Forbids to have some module level variables. Reasoning: We discourage using module variables like ``__author__``, because code should not contain any metadata. Solution: Place all the metadata in ``setup.py``, ``setup.cfg``, or ``pyproject.toml``. Use proper docstrings and packaging classifiers. Use ``pkg_resources`` if you need to import this data into your app. See :py:data:`~wemake_python_styleguide.constants.MODULE_METADATA_VARIABLES_BLACKLIST` for full list of bad names. Example:: # Wrong: __author__ = 'Nikita Sobolev' __version__ = 0.1.2 .. versionadded:: 0.1.0 Note: Returns Z410 as error code """ #: Error message shown to the user. error_template = 'Found wrong metadata variable {0}' code = 410 @final class EmptyModuleViolation(ASTViolation): """ Forbids to have empty modules. Reasoning: Why is it even there? Do not polute your project with empty files. Solution: If you have an empty module there are two ways to handle that: 1. delete it 2. drop some documentation in it, so you will explain why it is there .. versionadded:: 0.1.0 Note: Returns Z411 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found empty module' code = 411 @final class InitModuleHasLogicViolation(ASTViolation): """ Forbids to have logic inside ``__init__`` module. Reasoning: If you have logic inside the ``__init__`` module it means several things: 1. you are keeping some outdated stuff there, you need to refactor 2. you are placing this logic into the wrong file, just create another one 3. you are doing some dark magic, and you should not do that Solution: Put your code in other modules. However, we allow to have some contents inside the ``__init__`` module: 1. comments, since they are dropped before AST comes in play 2. docs string, because sometimes it is required to state something .. versionadded:: 0.1.0 Note: Returns Z412 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found `__init__` module with logic' code = 412 # Modules: @final class WrongKeywordViolation(ASTViolation): """ Forbids to use some keywords from ``python``. Reasoning: We believe that some keywords are anti-patterns. They promote bad-practices like ``global`` and ``pass``, or just not user-friendly like ``del``. Solution: Solutions differ from keyword to keyword. ``pass`` should be replaced with docstring or ``contextlib.suppress``. ``del`` should be replaced with specialized methods like ``.pop()``. ``global`` and ``nonlocal`` usages should be refactored. Example:: # Wrong: pass del nonlocal global .. versionadded:: 0.1.0 Note: Returns Z420 as error code """ #: Error message shown to the user. error_template = 'Found wrong keyword "{0}"' code = 420 @final class WrongFunctionCallViolation(ASTViolation): """ Forbids to call some built-in functions. Reasoning: Some functions are only suitable for very specific use cases, we forbid to use them in a free manner. See :py:data:`~wemake_python_styleguide.constants.FUNCTIONS_BLACKLIST` for the full list of blacklisted functions. See also: https://www.youtube.com/watch?v=YjHsOrOOSuI .. versionadded:: 0.1.0 Note: Returns Z421 as error code """ #: Error message shown to the user. error_template = 'Found wrong function call "{0}"' code = 421 @final class FutureImportViolation(ASTViolation): """ Forbids to use ``__future__`` imports. Reasoning: Almost all ``__future__`` imports are legacy ``python2`` compatibility tools that are no longer required. Solution: Remove them. Drop ``python2`` support. Except, there are some new ones for ``python4`` support. See :py:data:`~wemake_python_styleguide.constants.FUTURE_IMPORTS_WHITELIST` for the full list of allowed future imports. Example:: # Correct: from __future__ import annotations # Wrong: from __future__ import print_function .. versionadded:: 0.1.0 Note: Returns Z422 as error code """ #: Error message shown to the user. error_template = 'Found future import "{0}"' code = 422 @final class RaiseNotImplementedViolation(ASTViolation): """ Forbids to use ``NotImplemented`` error. Reasoning: These two violations look so similar. But, these violations have different use cases. Use cases of ``NotImplemented`` is too limited to be generally available. Solution: Use ``NotImplementedError``. Example:: # Correct: raise NotImplementedError('To be done') # Wrong: raise NotImplemented See Also: https://stackoverflow.com/a/44575926/4842742 .. versionadded:: 0.1.0 Note: Returns Z423 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found raise NotImplemented' code = 423 @final class BaseExceptionViolation(ASTViolation): """ Forbids to use ``BaseException`` exception. Reasoning: We can silence system exit and keyboard interrupt with this exception handler. It is almost the same as raw ``except:`` block. Solution: Handle ``Exception``, ``KeyboardInterrupt``, ``GeneratorExit``, and ``SystemExit`` separately. Do not use the plain ``except:`` keyword. Example:: # Correct: except Exception as ex: ... # Wrong: except BaseException as ex: ... See Also: https://docs.python.org/3/library/exceptions.html#exception-hierarchy https://help.semmle.com/wiki/pages/viewpage.action?pageId=1608527 .. versionadded:: 0.3.0 Note: Returns Z424 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found except `BaseException`' code = 424 # Design: @final class NestedFunctionViolation(ASTViolation): """ Forbids to have nested functions. Reasoning: Nesting functions is a bad practice. It is hard to test them, it is hard then to separate them. People tend to overuse closures, so it's hard to manage the dataflow. Solution: Just write flat functions, there's no need to nest them. Pass parameters as normal arguments, do not use closures. Until you need them for decorators or factories. We also disallow to nest ``lambda`` and ``async`` functions. See :py:data:`~wemake_python_styleguide.constants.NESTED_FUNCTIONS_WHITELIST` for the whole list of whitelisted names. Example:: # Correct: def do_some(): ... def other(): ... # Wrong: def do_some(): def inner(): ... .. versionadded:: 0.1.0 Note: Returns Z430 as error code """ #: Error message shown to the user. error_template = 'Found nested function "{0}"' code = 430 @final class NestedClassViolation(ASTViolation): """ Forbids to use nested classes. Reasoning: Nested classes are really hard to manage. You can not even create an instance of this class in many cases. Testing them is also really hard. Solution: Just write flat classes, there's no need nest them. If you are nesting classes inside a function for parametrization, then you will probably need to use different design (or metaclasses). See :py:data:`~wemake_python_styleguide.constants.NESTED_CLASSES_WHITELIST` for the full list of whitelisted names. Example:: # Correct: class Some(object): ... class Other(object): ... # Wrong: class Some(object): class Inner(object): ... .. versionadded:: 0.1.0 Note: Returns Z431 as error code """ #: Error message shown to the user. error_template = 'Found nested class "{0}"' code = 431 @final class MagicNumberViolation(ASTViolation): """ Forbids to use magic numbers in your code. What we call a "magic number"? Well, it is actually any number that appears in your code out of nowhere. Like ``42``. Or ``0.32``. Reasoning: It is very hard to remember what these numbers actually mean. Why were they used? Should they ever be changed? Or are they eternal like ``3.14``? Solution: Give these numbers a name! Move them to a separate variable, giving more context to the reader. And by moving things into new variables you will trigger other complexity checks. Example:: # Correct: price_in_euro = 3.33 # could be changed later total = get_items_from_cart() * price_in_euro # Wrong: total = get_items_from_cart() * 3.33 What are numbers that we exclude from this check? Any numbers that are assigned to a variable, array, dictionary, or keyword arguments inside a function. ``int`` numbers that are in range ``[-10, 10]`` and some other common numbers, that are defined in :py:data:`~wemake_python_styleguide.constants.MAGIC_NUMBERS_WHITELIST` See also: https://en.wikipedia.org/wiki/Magic_number_(programming) .. versionadded:: 0.1.0 Note: Returns Z432 as error code """ code = 432 #: Error message shown to the user. error_template = 'Found magic number: {0}' @final class StaticMethodViolation(ASTViolation): """ Forbids to use ``@staticmethod`` decorator. Reasoning: Static methods are not required to be inside the class. Because they even do not have access to the current instance. Solution: Use instance methods, ``@classmethod``, or functions instead. .. versionadded:: 0.1.0 Note: Returns Z433 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found using `@staticmethod`' code = 433 @final class BadMagicMethodViolation(ASTViolation): """ Forbids to use some magic methods. Reasoning: We forbid to use magic methods related to the forbidden language parts. Likewise, we forbid to use ``del`` keyword, so we forbid to use all magic methods related to it. Solution: Refactor you code to use custom methods instead. It will give more context to your app. See :py:data:`~wemake_python_styleguide.constants.MAGIC_METHODS_BLACKLIST` for the full blacklist of the magic methods. See also: https://www.youtube.com/watch?v=F6u5rhUQ6dU .. versionadded:: 0.1.0 Note: Returns Z434 as error code """ #: Error message shown to the user. error_template = 'Found using restricted magic method "{0}"' code = 434 @final class NestedImportViolation(ASTViolation): """ Forbids to have nested imports in functions. Reasoning: Usually nested imports are used to fix the import cycle. So, nested imports show that there's an issue with you design. Solution: You don't need nested imports, you need to refactor your code. Introduce a new module or find another way to do what you want to do. Rethink how your layered architecture should look like. Example:: # Correct: from my_module import some_function def some(): ... # Wrong: def some(): from my_module import some_function See also: https://github.com/seddonym/layer_linter .. versionadded:: 0.1.0 Note: Returns Z435 as error code """ #: Error message shown to the user. error_template = 'Found nested import "{0}"' code = 435 @final class RedundantForElseViolation(ASTViolation): """ Forbids to use ``else`` without ``break`` in ``for`` loop. Reasoning: When there's no ``break`` keyword in ``for`` body it means that ``else`` will always be called. This rule will reduce complexity, improve readability, and protect from possible errors. Solution: Refactor your ``else`` case logic to be inside the ``for`` body. Example:: # Correct: for letter in 'abc': if letter == 'b': break else: print('"b" is not found') # Wrong: for letter in 'abc': print(letter) else: print('"b" is not found') .. versionadded:: 0.3.0 Note: Returns Z436 as error code """ #: Error message shown to the user. error_template = 'Found `else` in `for` loop without `break`' code = 436 @final class RedundantFinallyViolation(ASTViolation): """ Forbids to use ``finally`` in ``try`` block without ``except`` block. Reasoning: This rule will reduce complexity and improve readability. Solution: Refactor your ``try`` logic. Replace the ``try-finally`` statement with a ``with`` statement. Example:: # Correct: with open("filename") as f: f.write(...) # Wrong: try: f = open("filename") f.write(...) finally: f.close() .. versionadded:: 0.3.0 Note: Returns Z437 as error code """ #: Error message shown to the user. error_template = 'Found `finally` in `try` block without `except`' code = 437 @final class ReassigningVariableToItselfViolation(ASTViolation): """ Forbids to assign variable to itself. Reasoning: There is no need to do that. Generally it is an indication of some error or just dead code. Example:: # Correct: some = some + 1 x_coord, y_coord = y_coord, x_coord # Wrong: some = some x_coord, y_coord = x_coord, y_coord .. versionadded:: 0.3.0 Note: Returns Z438 as error code """ #: Error message shown to the user. error_template = 'Found reassigning variable "{0}" to itself' code = 438 @final class YieldInsideInitViolation(ASTViolation): """ Forbids to use ``yield`` inside of ``__init__`` method. Reasoning: ``__init__`` should be used to initialize new objects. It shouldn't ``yield`` anything, because it should return ``None`` by the convention. Example:: # Correct: class Example(object): def __init__(self): self._public_items_count = 0 # Wrong: class Example(object): def __init__(self): yield 10 .. versionadded:: 0.3.0 Note: Returns Z439 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found `yield` inside `__init__`' code = 439 @final class ProtectedModuleViolation(ASTViolation): """ Forbids to ``import`` protected modules. Reasoning: When importing protected modules we break a contract that authors of this module enforce. This way we are not respecting encapsulation and it may break our code at any moment. Solution: Do not import anything from protected modules. Respect the encapsulation. Example:: # Correct: from some.public.module import FooClass # Wrong: import _compat from some._protected.module import BarClass from some.module import _protected .. versionadded:: 0.3.0 Note: Returns Z440 as error code """ #: Error message shown to the user. error_template = 'Found protected module import "{0}"' code = 440 @final class ProtectedAttributeViolation(ASTViolation): """ Forbids to use protected attributes and methods. Reasoning: When using protected attributes and method we break a contract that authors of this class enforce. This way we are not respecting encapsulation and it may break our code at any moment. Solution: Do not use protected attributes and methods. Respect the encapsulation. Example:: # Correct: self._protected = 1 cls._hidden_method() some.public() # Wrong: print(some._protected) instance._hidden() self.container._internal = 10 Note, that it is possible to use protected attributes with ``self`` and ``cls`` as base names. We allow this so you can create and use protected attributes and methods inside the class context. This is how protected attributes should be used. .. versionadded:: 0.3.0 Note: Returns Z441 as error code """ #: Error message shown to the user. error_template = 'Found protected attribute usage "{0}"' code = 441 PK!pk8>8>1wemake_python_styleguide/violations/complexity.py# -*- coding: utf-8 -*- """ These checks finds flaws in your application design. We try to stick to "the magical 7 ± 2 number" when counting things. https://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two That's how many objects we can keep in our memory at a time. We try hard not to exceed the memory capacity limit. You can also find interesting reading about "Cognitive complexity": https://www.sonarsource.com/docs/CognitiveComplexity.pdf Note: Simple is better than complex. Complex is better than complicated. .. currentmodule:: wemake_python_styleguide.violations.complexity Summary ------- .. autosummary:: :nosignatures: JonesScoreViolation TooManyImportsViolation TooManyModuleMembersViolation TooManyLocalsViolation TooManyArgumentsViolation TooManyReturnsViolation TooManyExpressionsViolation TooManyMethodsViolation TooDeepNestingViolation LineComplexityViolation TooManyConditionsViolation TooManyElifsViolation TooManyForsInComprehensionViolation TooManyBaseClassesViolation Module complexity ----------------- .. autoclass:: JonesScoreViolation .. autoclass:: TooManyImportsViolation .. autoclass:: TooManyModuleMembersViolation .. autoclass:: TooManyBaseClassesViolation Function and class complexity ----------------------------- .. autoclass:: TooManyLocalsViolation .. autoclass:: TooManyArgumentsViolation .. autoclass:: TooManyReturnsViolation .. autoclass:: TooManyExpressionsViolation .. autoclass:: TooManyMethodsViolation Structures complexity --------------------- .. autoclass:: TooDeepNestingViolation .. autoclass:: LineComplexityViolation .. autoclass:: TooManyConditionsViolation .. autoclass:: TooManyElifsViolation .. autoclass:: TooManyForsInComprehensionViolation """ from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.base import ( ASTViolation, SimpleViolation, ) @final class JonesScoreViolation(SimpleViolation): """ Forbids to have modules with complex lines. We are using Jones Complexity algorithm to count module's score. See :py:class:`~.LineComplexityViolation` for details of per-line-complexity. How it is done: we count complexity per line, then measuring the median complexity across the lines in the whole module. Reasoning: Having complex modules will decrease your code maintainability. Solution: Refactor the module contents. See also: https://github.com/Miserlou/JonesComplexity This rule is configurable with ``--max-module-score``. .. versionadded:: 0.1.0 Note: Returns Z200 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found module with high Jones Complexity score' code = 200 @final class TooManyImportsViolation(SimpleViolation): """ Forbids to have modules with too many imports. Namespaces are one honking great idea -- let's do more of those! Reasoning: Having too many imports without prefixes is quite expensive. You have to memorize all the source locations of the imports. And sometimes it is hard to remember what kind of functions and classes are already injected into your context. It is also a questionable design if a single module has a lot of imports. Why a single module has so many dependencies? So, it becomes too coupled. Solution: Refactor the imports to import a common namespace. Something like ``from package import module`` and then use it like ``module.function()``. Or refactor your code and split the complex module into several ones. We do not make any differences between ``import`` and ``from ... import ...``. This rule is configurable with ``--max-imports``. .. versionadded:: 0.1.0 Note: Returns Z201 as error code """ #: Error message shown to the user. error_template = 'Found module with too many imports: {0}' code = 201 @final class TooManyModuleMembersViolation(SimpleViolation): """ Forbids to have many classes and functions in a single module. Reasoning: Having many classes and functions in a single module is a bad thing. Soon it will be hard to read through this code and understand it. Solution: It is better to split this module into several modules or a package. We do not make any differences between classes and functions in this check. They are treated as the same unit of logic. We also do not care about functions and classes being public or not. However, methods are counted separately on a per-class basis. This rule is configurable with ``--max-module-members``. .. versionadded:: 0.1.0 Note: Returns Z202 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found too many module members' code = 202 # Functions and classes: @final class TooManyLocalsViolation(ASTViolation): """ Forbids to have too many local variables in the unit of code. Reasoning: Having too many variables in a single function is bad thing. Soon, you will find troubles to understand what this variable means. It will also become hard to name new variables. Solution: If you have too many variables in a function, you have to refactor it. What counts as a local variable? We only count variable as local in the following case: it is assigned inside the function body. We do not count variables defined inside comprehensions as local variables, since it is impossible to use them outside of the comprehension. Example:: def first_function(param): first_var = 1 def second_function(argument): second_var = 1 argument = int(argument) third_var, _ = some_call() In this example we will count as locals only several variables: 1. ``first_var``, because it is assigned inside the function's body 2. ``second_var``, because it is assigned inside the function's body 3. ``argument``, because it is reassigned inside the function's body 4. ``third_var``, because it is assigned inside the function's body Please, note that ``_`` is a special case. It is not counted as a local variable. Since by design it means: do not count me as a real variable. This rule is configurable with ``--max-local-variables``. .. versionadded:: 0.1.0 Note: Returns Z210 as error code """ #: Error message shown to the user. error_template = 'Found too many local variables "{0}"' code = 210 @final class TooManyArgumentsViolation(ASTViolation): """ Forbids to have too many arguments for a function or method. Reasoning: This is an indicator of a bad design. When function requires many arguments it shows that it is required to refactor this piece of code. It also indicates that function does too many things at once. Solution: Split function into several functions. Then it will be easier to use them. This rule is configurable with ``--max-arguments``. .. versionadded:: 0.1.0 Note: Returns Z211 as error code """ #: Error message shown to the user. error_template = 'Found too many arguments "{0}"' code = 211 @final class TooManyReturnsViolation(ASTViolation): """ Forbids placing too many ``return`` statements into the function. Reasoning: When there are too many ``return`` keywords, functions are hard to test. They are also hard to read and hard to change and keep everything inside your head at once. Solution: Change your design. This rule is configurable with ``--max-returns``. .. versionadded:: 0.1.0 Note: Returns Z212 as error code """ #: Error message shown to the user. error_template = 'Found too many return statements "{0}"' code = 212 @final class TooManyExpressionsViolation(ASTViolation): """ Forbids putting too many expressions in a unit of code. Reasoning: When there are too many expressions it means that this specific function does too many things at once. It has too much logic. Solution: Split function into several functions, refactor your API. This rule is configurable with ``--max-expressions``. .. versionadded:: 0.1.0 Note: Returns Z213 as error code """ #: Error message shown to the user. error_template = 'Found too many expressions "{0}"' code = 213 @final class TooManyMethodsViolation(ASTViolation): """ Forbids to have many methods in a single class. Reasoning: Having too many methods might lead to the "God object". This kind of objects can handle everything. So, in the end your code becomes too hard to maintain and test. Solution: What to do if you have too many methods in a single class? Split this class into several classes. Then use composition or inheritance to refactor your code. This will protect you from "God object" anti-pattern. See: https://en.wikipedia.org/wiki/God_object We do not make any difference between instance and class methods. We also do not care about functions and classes being public or not. We also do not count inherited methods from parents. This rule does not count attributes of a class. This rule is configurable with ``--max-methods``. .. versionadded:: 0.1.0 Note: Returns Z214 as error code """ #: Error message shown to the user. error_template = 'Found too many methods "{0}"' code = 214 # Structures: @final class TooDeepNestingViolation(ASTViolation): """ Forbids nesting blocks too deep. Reasoning: If nesting is too deep that indicates usage of a complex logic and language constructions. This means that our design is not suited to handle such construction. Solution: We need to refactor our complex construction into simpler ones. We can use new functions or different constructions. This rule is configurable with ``--max-offset-blocks``. .. versionadded:: 0.1.0 Note: Returns Z220 as error code """ #: Error message shown to the user. error_template = 'Found too deep nesting "{0}"' code = 220 @final class LineComplexityViolation(ASTViolation): """ Forbids to have complex lines. We are using Jones Complexity algorithm to count complexity. What is Jones Complexity? It is a simple yet powerful method to count the number of ``ast`` nodes per line. If the complexity of a single line is higher than a threshold, then an error is raised. What nodes do we count? All except the following: 1. modules 2. function and classes, since they are checked differently 3. type annotations, since they do not increase complexity Reasoning: Having a complex line indicates that you somehow managed to put too much logic inside a single line. At some point in time you will no longer be able to understand what this line means and what it does. Solution: Split a single line into several lines: by creating new variables, statements or functions. Note, this might trigger new complexity issues. With this technique a single new node in a line might trigger a complex refactoring process including several modules. See also: https://github.com/Miserlou/JonesComplexity This rule is configurable with ``--max-line-complexity``. .. versionadded:: 0.1.0 Note: Returns Z221 as error code """ #: Error message shown to the user. error_template = 'Found line with high Jones Complexity: {0}' code = 221 @final class TooManyConditionsViolation(ASTViolation): """ Forbids to have conditions with too many logical operators. Reasoning: When reading through the complex conditions you will fail to understand all the possible branches. And you will end up putting debug breakpoint on this line just to figure out how it works. Solution: We can reduce the complexity of a single ``if`` by doing two things: creating new variables or creating nested ``if`` statements. Both of these actions will trigger other complexity checks. We only check ``if`` and ``while`` nodes for this type of complexity. We check ``if`` nodes inside list comprehensions and ternary expressions. We count ``and`` and ``or`` keywords as conditions. Example:: # The next line has 2 conditions: if x_coord > 1 and x_coord < 10: ... This rule is configurable with ``--max-conditions``. .. versionadded:: 0.1.0 Note: Returns Z222 as error code """ #: Error message shown to the user. error_template = 'Found a condition with too much logic: {0}' code = 222 @final class TooManyElifsViolation(ASTViolation): """ Forbids to use many ``elif`` branches. Reasoning: This rule is specifically important, because many ``elif`` branches indicate a complex flow in your design: you are reimplementing ``switch`` in python. Solution: There are different design patters to use instead. For example, you can use some interface that just call a specific method without ``if``. This rule is configurable with ``--max-elifs``. .. versionadded:: 0.1.0 Note: Returns Z223 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found too many `elif` branches' code = 223 @final class TooManyForsInComprehensionViolation(ASTViolation): """ Forbids to have too many ``for`` statement within a comprehension. Reasoning: When reading through the complex comprehension you will fail to understand it. Solution: We can reduce the complexity of a comprehension by reducing the amount of ``for`` statements. Refactor your code to use several ``for`` loops, comprehensions, or different functions. Example:: # Wrong: ast_nodes = [ target for assignment in top_level_assigns for target in assignment.targets for _ in range(10) ] .. versionadded:: 0.3.0 Note: Returns Z224 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found a comprehension with too many `for` statements' code = 224 class TooManyBaseClassesViolation(ASTViolation): """ Restrict the maximum number of base classes. Reasoning: It is almost never possible to navigate to the desired method of a parent class when you need it with multiple mixins. It is hard to understand ``mro`` and ``super`` calls. Do not overuse this technique. Solution: Restrict the number of base classes. This rule is configurable with ``--max-base-classes``. Example:: # Correct: class SomeClassName(FirstParentClass, SecondParentClass): ... # Wrong: class SomeClassName( FirstParentClass, SecondParentClass, ThirdParentClass, CustomClass, AddedClass, ): ... .. versionadded:: 0.3.0 Note: Returns Z225 as error code """ should_use_text = False #: Error message shown to the user error_template = 'Too many number of base classes' code = 225 PK!u4A992wemake_python_styleguide/violations/consistency.py# -*- coding: utf-8 -*- """ These checks limit the Python's inconsistency. We can do the same things differently in Python. For example, there are three ways to format a string. There are several ways to write the same number. We like our code to be consistent. It is easier to bare with your code base if you follow these rules. .. currentmodule:: wemake_python_styleguide.violations.consistency Summary ------- .. autosummary:: :nosignatures: LocalFolderImportViolation DottedRawImportViolation UnicodeStringViolation UnderscoredNumberViolation PartialFloatViolation FormattedStringViolation RequiredBaseClassViolation MultipleIfsInComprehensionViolation ConstantComparisonViolation BadNumberSuffixViolation ComparisonOrderViolation MultipleInComparisonViolation RedundantComparisonViolation WrongConditionalViolation MissingSpaceBetweenKeywordAndParenViolation ObjectInBaseClassesListViolation Consistency checks ------------------ .. autoclass:: LocalFolderImportViolation .. autoclass:: DottedRawImportViolation .. autoclass:: UnicodeStringViolation .. autoclass:: UnderscoredNumberViolation .. autoclass:: PartialFloatViolation .. autoclass:: FormattedStringViolation .. autoclass:: RequiredBaseClassViolation .. autoclass:: MultipleIfsInComprehensionViolation .. autoclass:: ConstantComparisonViolation .. autoclass:: BadNumberSuffixViolation .. autoclass:: ComparisonOrderViolation .. autoclass:: MultipleInComparisonViolation .. autoclass:: RedundantComparisonViolation .. autoclass:: WrongConditionalViolation .. autoclass:: MissingSpaceBetweenKeywordAndParenViolation .. autoclass:: ObjectInBaseClassesListViolation """ from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.base import ( ASTViolation, TokenizeViolation, ) @final class LocalFolderImportViolation(ASTViolation): """ Forbids to have imports relative to the current folder. Reasoning: We should pick one style and stick to it. We have decided to use the explicit one. Example:: # Correct: from my_package.version import get_version # Wrong: from .version import get_version from ..drivers import MySQLDriver .. versionadded:: 0.1.0 Note: Returns Z300 as error code """ #: Error message shown to the user. error_template = 'Found local folder import "{0}"' code = 300 @final class DottedRawImportViolation(ASTViolation): """ Forbids to use imports like ``import os.path``. Reasoning: There too many different ways to import something. We should pick one style and stick to it. We have decided to use the readable one. Solution: Refactor your import statement. Example:: # Correct: from os import path # Wrong: import os.path .. versionadded:: 0.1.0 Note: Returns Z301 as error code """ #: Error message shown to the user. error_template = 'Found dotted raw import "{0}"' code = 301 @final class UnicodeStringViolation(TokenizeViolation): """ Forbids to use ``u`` string prefix. Reasoning: We do not need this prefix since ``python2``. But, it is still possible to find it inside the codebase. Solution: Remove this prefix. Example:: # Correct: nickname = 'sobolevn' file_contents = b'aabbcc' # Wrong: nickname = u'sobolevn' .. versionadded:: 0.1.0 Note: Returns Z302 as error code """ code = 302 #: Error message shown to the user. error_template = 'Found unicode string prefix: {0}' @final class UnderscoredNumberViolation(TokenizeViolation): """ Forbids to use underscores (``_``) in numbers. Reasoning: It is possible to write ``1000`` in three different ways: ``1_000``, ``10_00``, and ``100_0``. And it would be still the same number. Count how many ways there are to write bigger numbers. Currently, it all depends on cultural habits of the author. We enforce a single way to write numbers: without the underscore. Solution: Numbers should be written as numbers: ``1000``. If you have a very big number with a lot of zeros, use multiplication. Example:: # Correct: phone = 88313443 million = 1000000 # Wrong: phone = 8_83_134_43 million = 100_00_00 .. versionadded:: 0.1.0 Note: Returns Z303 as error code """ code = 303 #: Error message shown to the user. error_template = 'Found underscored number: {0}' @final class PartialFloatViolation(TokenizeViolation): """ Forbids to use partial floats like ``.05`` or ``23.``. Reasoning: Partial numbers are hard to read and they can be confused with other numbers. For example, it is really easy to confuse ``0.5`` and ``.05`` when reading through the source code. Solution: Use full versions with leading and starting zeros. Example:: # Correct: half = 0.5 ten_float = 10.0 # Wrong: half = .5 ten_float = 10. .. versionadded:: 0.1.0 Note: Returns Z304 as error code """ code = 304 #: Error message shown to the user. error_template = 'Found partial float: {0}' @final class FormattedStringViolation(ASTViolation): """ Forbids to use ``f`` strings. Reasoning: ``f`` strings looses context too often and they are hard to lint. Imagine that you have a string that breaks when you move it two lines above. That's not how a string should behave. Also, they promote a bad practice: putting your logic inside the template. Solution: Use ``.format()`` with indexed params instead. See also: https://github.com/xZise/flake8-string-format Example:: # Wrong: f'Result is: {2 + 2}' # Correct: 'Result is: {0}'.format(2 + 2) 'Hey {user}! How are you?'.format(user='sobolevn') .. versionadded:: 0.1.0 Note: Returns Z305 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found `f` string' code = 305 @final class RequiredBaseClassViolation(ASTViolation): """ Forbids to write classes without base classes. Reasoning: We just need to decide how to do it. We need a single and unified rule about base classes. We have decided to stick to the explicit base class notation. Solution: Add a base class. Example:: # Correct: class Some(object): ... # Wrong: class Some: ... .. versionadded:: 0.1.0 Note: Returns Z306 as error code """ #: Error message shown to the user. error_template = 'Found class without a base class "{0}"' code = 306 @final class MultipleIfsInComprehensionViolation(ASTViolation): """ Forbids to have multiple ``if`` statements inside list comprehensions. Reasoning: It is very hard to read multiple ``if`` statements inside a list comprehension. Since, it is even hard to tell all of them should pass or fail. Solution: Use a single ``if`` statement inside list comprehensions. Use ``filter()`` if you have complicated logic. Example:: # Wrong: nodes = [node for node in html if node != 'b' if node != 'i'] # Correct: nodes = [node for node in html if node not in ('b', 'i')] .. versionadded:: 0.1.0 Note: Returns Z307 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found list comprehension with multiple `if`s' code = 307 @final class ConstantComparisonViolation(ASTViolation): """ Forbids to have comparisons between two literals. Reasoning: When two constants are compared it is typically an indication of a mistake, since the Boolean value of the comparison will always be the same. Solution: Remove the constant comparison and any associated dead code. Example:: # Wrong: if 60 * 60 < 1000: do_something() else: do_something_else() # Correct: do_something_else() .. versionadded:: 0.3.0 Note: Returns Z308 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found constant comparison' code = 308 @final class ComparisonOrderViolation(ASTViolation): """ Forbids comparision where argument doesn't come first. Reasoning: It is hard to read the code when you have to shuffle ordering of the arguments all the time. Bring a consistency to the comparison! Solution: Refactor your comparison expression, place the argument first. Example:: # Correct: if some_x > 3: if 3 < some_x < 10: # Wrong: if 3 < some_x: .. versionadded:: 0.3.0 Note: Returns Z309 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found reversed comparison order' code = 309 @final class BadNumberSuffixViolation(TokenizeViolation): """ Forbids to use capital ``X``, ``O``, ``B``, and ``E`` in numbers. Reasoning: Octal, hex, binary and scientific notation suffixes could be written in two possible notations: lowercase and uppercase. Which brings confusion and decreases code consistency and readability. We enforce a single way to write numbers with suffixes: suffix with lowercase chars. Solution: Octal, hex, binary and scientific notation suffixes in numbers should be written lowercase. Example:: # Correct: hex_number = 0xFF octal_number = 0o11 binary_number = 0b1001 number_with_scientific_notation = 1.5e+10 # Wrong: hex_number = 0XFF octal_number = 0O11 binary_number = 0B1001 number_with_scientific_notation = 1.5E+10 .. versionadded:: 0.3.0 Note: Returns Z310 as error code """ #: Error message shown to the user. error_template = 'Found underscored number: {0}' code = 310 @final class MultipleInComparisonViolation(ASTViolation): """ Forbids comparision where multiple ``in`` checks. Reasoning: Using multiple ``in`` is unreadable. Solution: Refactor your comparison expression to use several ``and`` conditions or separate ``if`` statements in case it is appropriate. Example:: # Correct: if item in bucket and bucket in master_list_of_buckets: if x_coord in line and line in square: # Wrong: if item in bucket in master_list_of_buckets: if x_cord in line in square: .. versionadded:: 0.3.0 Note: Returns Z311 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found multiple `in` comparisons' code = 311 @final class RedundantComparisonViolation(ASTViolation): """ Forbids to have comparisons between the same variable. Reasoning: When the same variables are compared it is typically an indication of a mistake, since the Boolean value of the comparison will always be the same. Solution: Remove the same variable comparison and any associated dead code. Example:: # Wrong: a = 1 if a < a: do_something() else: do_something_else() # Correct: do_something() .. versionadded:: 0.3.0 Note: Returns Z312 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found comparison between same variable' code = 312 @final class MissingSpaceBetweenKeywordAndParenViolation(TokenizeViolation): """ Forbid opening parenthesis from following keyword without space in between. Reasoning: Some people use ``return`` and ``yield`` keywords as functions. The same happened to good old ``print`` in Python2. Solution: Insert space symbol between keyword and open paren. Example:: # Wrong: def func(): a = 1 b = 2 del(a, b) yield(1, 2, 3) # Correct: def func(): a = 1 del (a, b) yield (1, 2, 3) .. versionadded:: 0.3.0 Note: Returns Z313 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found parens right after a keyword' code = 313 class WrongConditionalViolation(ASTViolation): """ Forbids using ``if`` statements that use invalid conditionals. Reasoning: When invalid conditional arguments are used it is typically an indication of a mistake, since the value of the conditional result will always be the same. Solution: Remove the conditional and any associated dead code. Example:: # Correct: if value is True: ... # Wrong: if True: ... .. versionadded:: 0.3.0 Note: Returns Z314 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Conditional always evaluates to same result' code = 314 class ObjectInBaseClassesListViolation(ASTViolation): """ Forbid extra ``object`` in parent classes list. Reasoning: We should allow object only when we explicitly use it as a single parent class. When there is an other class or there are multiple parents - we should not allow it for the consistency reasons. Solution: Remove extra ``object`` parent class from the list. Example:: # Correct: class SomeClassName(object): ... class SomeClassName(FirstParentClass, SecondParentClass): ... # Wrong: class SomeClassName(FirstParentClass, SecondParentClass, object): ... .. versionadded:: 0.3.0 Note: Returns Z315 as error code """ should_use_text = False #: Error message shown to the user error_template = 'Founded extra `object` in parent classes list' code = 315 PK! =4=4-wemake_python_styleguide/violations/naming.py# -*- coding: utf-8 -*- """ Naming is hard! It is in fact one of the two hardest problems. These checks are required to make your application easier to read and understand by multiple people over the long period of time. Naming convention ----------------- Our naming convention tries to cover all possible cases. It is partially automated with this linter, but - Some rules are still WIP - Some rules will never be automated, code reviews to the rescue! General ~~~~~~~ - Use clear names, do not use words that do not mean anything like ``obj`` - Use names of an appropriate length: not too short, not too long - Protected members should use underscore as the first char - Private names are not allowed - Do not use consecutive underscores - When writing abbreviations in ``UpperCase`` capitalize all letters: ``HTTPAddress`` - When writting abbreviations in ``snake_case`` use lowercase: ``http_address`` - When writting numbers in ``snake_case`` do not use extra ``_`` before numbers as in ``http2_protocol`` Packages ~~~~~~~~ - Packages should use ``snake_case`` - One word for a package is the most prefitable name Modules ~~~~~~~ - Modules should use ``snake_case`` - Module names should not be too short - Module names should not overuse magic names - Module names should be valid Python variable names Classes ~~~~~~~ - Classes should use ``UpperCase`` - Python's built-in classes, however are typically lowercase words - Exception classes should end with ``Error`` Instance attributes ~~~~~~~~~~~~~~~~~~~ - Instance attributes should use ``snake_case`` with no exceptions Class attributes ~~~~~~~~~~~~~~~~ - Class attributes should use ``snake_case`` with no exceptions Functions and methods ~~~~~~~~~~~~~~~~~~~~~ - Functions and methods should use ``snake_case`` with no exceptions Method arguments ~~~~~~~~~~~~~~~~ - Instance methods should have their first argument named ``self`` - Class methods should have their first argument named ``cls`` - Metaclass methods should have their first argument named ``mcs`` - When argument is unused it should be prefixed with an underscore - Python's ``*args`` and ``**kwargs`` should be default names when just passing these values to some other method/function - Unless you want to use these values in place, then name them explicitly - Keyword-only arguments must be separated from other arguments with ``*`` Global (module level) variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Global variables should use ``CONSTANT_CASE`` - Unless other is required by the API, example: ``urlpatterns`` in Django Variables ~~~~~~~~~ - Variables should use ``snake_case`` with no exceptions - When some variable is unused it should be prefixed with an underscore Type aliases ~~~~~~~~~~~~ - Should use ``UpperCase`` as real classes - Should not contain word ``type`` in its name - Generic types should be called ``TT`` or ``KT`` or ``VT`` - Covariant and contravariant types should be marked with ``Cov`` and ``Contra`` suffixes - In this case one letter can be dropped: ``TCov`` and ``KContra`` .. currentmodule:: wemake_python_styleguide.violations.naming Summary ------- .. autosummary:: :nosignatures: WrongModuleNameViolation WrongModuleMagicNameViolation TooShortModuleNameViolation WrongModuleNameUnderscoresViolation WrongModuleNamePatternViolation WrongVariableNameViolation TooShortVariableNameViolation PrivateNameViolation SameAliasImportViolation UnderScoredNumberNameViolation UpperCaseAttributeViolation ConsecutiveUnderscoresInNameViolation Module names ------------ .. autoclass:: WrongModuleNameViolation .. autoclass:: WrongModuleMagicNameViolation .. autoclass:: TooShortModuleNameViolation .. autoclass:: WrongModuleNameUnderscoresViolation .. autoclass:: WrongModuleNamePatternViolation Variable names -------------- .. autoclass:: WrongVariableNameViolation .. autoclass:: TooShortVariableNameViolation .. autoclass:: PrivateNameViolation .. autoclass:: SameAliasImportViolation .. autoclass:: UnderScoredNumberNameViolation .. autoclass:: UpperCaseAttributeViolations .. autoclass:: ConsecutiveUnderscoresInNameViolation """ from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.base import ( ASTViolation, SimpleViolation, ) @final class WrongModuleNameViolation(SimpleViolation): """ Forbids to use blacklisted module names. Reasoning: Some module names are not expressive enough. It is hard to tell what you can find inside the ``utils.py`` module. Solution: Rename your module, reorganize the contents. See :py:data:`~wemake_python_styleguide.constants.MODULE_NAMES_BLACKLIST` for the full list of bad module names. Example:: # Correct: github.py views.py # Wrong: utils.py helpers.py .. versionadded:: 0.1.0 Note: Returns Z100 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found wrong module name' code = 100 @final class WrongModuleMagicNameViolation(SimpleViolation): """ Forbids to use any magic names except whitelisted ones. Reasoning: Do not fall in love with magic. There's no good reason to use magic names, when you can use regular names. See :py:data:`~wemake_python_styleguide.constants.MAGIC_MODULE_NAMES_WHITELIST` for the full list of allowed magic module names. Example:: # Correct: __init__.py __main__.py # Wrong: __version__.py .. versionadded:: 0.1.0 Note: Returns Z101 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found wrong module magic name' code = 101 @final class TooShortModuleNameViolation(SimpleViolation): """ Forbids to use module name shorter than some breakpoint. Reasoning: Too short module names are not expressive enough. We will have to open the code to find out what is going on there. Solution: Rename the module. This rule is configurable with ``--min-module-name-length``. .. versionadded:: 0.1.0 Note: Returns Z102 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found too short module name' code = 102 @final class WrongModuleNameUnderscoresViolation(SimpleViolation): """ Forbids to use multiple underscores in a row in a module name. Reasoning: It is hard to tell how many underscores are there: two or three? Solution: Keep just one underscore in a module name. Example:: # Correct: __init__.py some_module_name.py test.py # Wrong: some__wrong__name.py my__module.py __fake__magic__.py .. versionadded:: 0.1.0 Note: Returns Z103 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found repeating underscores in a module name' code = 103 @final class WrongModuleNamePatternViolation(SimpleViolation): """ Forbids to use module names that do not match our pattern. Reasoning: Just like the variable names - module names should be consistent. Ideally, they should follow the same rules. For ``python`` world it is common to use `snake_case` notation. We use :py:data:`~wemake_python_styleguide.constants.MODULE_NAME_PATTERN` to validate the module names. Example:: # Correct: __init__.py some_module_name.py test12.py # Wrong: _some.py MyModule.py 0001_migration.py .. versionadded:: 0.1.0 Note: Returns Z104 as error code """ should_use_text = False #: Error message shown to the user. error_template = 'Found incorrect module name pattern' code = 104 # Variables: @final class WrongVariableNameViolation(ASTViolation): """ Forbids to have blacklisted variable names. Reasoning: We have found some names that are not expressive enough. However, they appear in the code more than often. All names that we forbid to use could be improved. Solution: Try to use more specific name instead. If you really want to use any of the names from the list, add a prefix or suffix to it. It will serve you well. See :py:data:`~wemake_python_styleguide.constants.VARIABLE_NAMES_BLACKLIST` for the full list of blacklisted variable names. Example:: # Correct: html_node_item = None # Wrong: item = None .. versionadded:: 0.1.0 Note: Returns Z110 as error code """ #: Error message shown to the user. error_template = 'Found wrong variable name "{0}"' code = 110 @final class TooShortVariableNameViolation(ASTViolation): """ Forbids to have too short variable names. Reasoning: It is hard to understand what the variable means and why it is used, if it's name is too short. Solution: Think of another name. Give more context to it. This rule is configurable with ``--min-variable-length``. Example:: # Correct: x_coordinate = 1 abscissa = 2 # Wrong: x = 1 y = 2 .. versionadded:: 0.1.0 Note: Returns Z111 as error code """ #: Error message shown to the user. error_template = 'Found too short name "{0}"' code = 111 @final class PrivateNameViolation(ASTViolation): """ Forbids to have private name pattern. Reasoning: Private is not private in ``python``. So, why should we pretend it is? This might lead to some serious design flaws. Solution: Rename your variable or method to be protected. Think about your design, why do you want to make it private? Are there any other ways to achieve what you want? This rule checks: variables, attributes, functions, and methods. Example:: # Correct: def _collect_coverage(self): ... # Wrong: def __collect_coverage(self): ... Note: Returns Z112 as error code .. versionadded:: 0.1.0 """ #: Error message shown to the user. error_template = 'Found private name pattern "{0}"' code = 112 @final class SameAliasImportViolation(ASTViolation): """ Forbids to use the same alias as the original name in imports. Reasoning: Why would you even do this in the first place? Example:: # Correct: from os import path # Wrong: from os import path as path .. versionadded:: 0.1.0 Note: Returns Z113 as error code """ #: Error message shown to the user. error_template = 'Found same alias import "{0}"' code = 113 @final class UnderScoredNumberNameViolation(SimpleViolation): """ Forbids to have names with underscored numbers pattern. Reasoning: This is done for consistency in naming. Solution: Do not put an underscore between text and numbers, that is confusing. Rename your variable or modules to not include underscored numbers. This rule checks: variables, and modules. Please, note that putting an underscore that replaces ``-`` in some names between numbers is fine, example: ``ISO-123-456`` would became ``iso_123_456``. Example:: # Correct: star_wars_episode2 = 'awesome!' iso_123_456 = 'some data' # Wrong: star_wars_episode_2 = 'not so awesome' .. versionadded:: 0.3.0 Note: Returns Z114 as error code """ #: Error message shown to the user. error_template = 'Found underscored name pattern "{0}"' code = 114 @final class UpperCaseAttributeViolation(ASTViolation): """ Forbids to use anything but snake_case for naming attributes on a class. Reasoning: Constants with upper-case names belong on a module level. Solution: Move your constants to the module level. Rename your variables so that they conform to "snake_case" convention. Example:: # Correct: class A(object): my_constant = 42 # Wrong: class A(object): MY_CONSTANT = 42 .. versionadded:: 0.3.0 Note: Returns Z115 as error code """ #: Error message shown to the user. error_template = 'Found upper-case constant in a class "{0}"' code = 115 @final class ConsecutiveUnderscoresInNameViolation(ASTViolation): """ Forbids to use more than one consecutive underscore in variable names. Reasoning: This is done to gain extra readability. This naming rule already exist for module names. Example:: # Correct: some_value = 5 __magic__ = 5 # Wrong: some__value = 5 .. versionadded:: 0.3.0 Note: Returns Z116 as error code """ #: Error message shown to the user. error_template = 'Found consecutive underscores in a variable "{0}"' code = 116 PK!uh-wemake_python_styleguide/visitors/__init__.py# -*- coding: utf-8 -*- PK!uh1wemake_python_styleguide/visitors/ast/__init__.py# -*- coding: utf-8 -*- PK!ށ3wemake_python_styleguide/visitors/ast/attributes.py# -*- coding: utf-8 -*- import ast from typing import ClassVar, FrozenSet from wemake_python_styleguide.logics.variables import access from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.best_practices import ( ProtectedAttributeViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor @final class WrongAttributeVisitor(BaseNodeVisitor): """Ensures that attributes are used correctly.""" _allowed_to_use_protected: ClassVar[FrozenSet[str]] = frozenset(( 'self', 'cls', )) def _check_protected_attribute(self, node: ast.Attribute) -> None: if access.is_protected_variable(node.attr): if isinstance(node.value, ast.Name): if node.value.id in self._allowed_to_use_protected: return self.add_violation(ProtectedAttributeViolation(node)) def visit_Attribute(self, node: ast.Attribute) -> None: """ Checks the `Attribute` node. Raises: ProtectedAttributeViolation """ self._check_protected_attribute(node) self.generic_visit(node) PK!k%V V 1wemake_python_styleguide/visitors/ast/builtins.py# -*- coding: utf-8 -*- import ast from typing import ClassVar, Optional from wemake_python_styleguide import constants from wemake_python_styleguide.types import AnyNodes, final from wemake_python_styleguide.violations.best_practices import ( MagicNumberViolation, ) from wemake_python_styleguide.violations.consistency import ( FormattedStringViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor @final class WrongStringVisitor(BaseNodeVisitor): """Restricts to use ``f`` strings.""" def visit_JoinedStr(self, node: ast.JoinedStr) -> None: """ Restricts to use ``f`` strings. Raises: FormattedStringViolation """ self.add_violation(FormattedStringViolation(node)) self.generic_visit(node) @final class MagicNumberVisitor(BaseNodeVisitor): """Checks magic numbers used in the code.""" _allowed_parents: ClassVar[AnyNodes] = ( ast.Assign, # Constructor usages: ast.FunctionDef, ast.AsyncFunctionDef, ast.arguments, # Primitives: ast.List, ast.Dict, ast.Set, ast.Tuple, ) _proxy_parents: ClassVar[AnyNodes] = ( ast.UnaryOp, ) def _get_real_parent(self, node: Optional[ast.AST]) -> Optional[ast.AST]: """ Returns real number's parent. What can go wrong? 1. Number can be negative: ``x = -1``, so ``1`` has ``UnaryOp`` as parent, but should return ``Assign`` """ parent = getattr(node, 'parent', None) if isinstance(parent, self._proxy_parents): return self._get_real_parent(parent) return parent def _check_is_magic(self, node: ast.Num) -> None: parent = self._get_real_parent(node) if isinstance(parent, self._allowed_parents): return if node.n in constants.MAGIC_NUMBERS_WHITELIST: return if isinstance(node.n, int) and node.n <= constants.NON_MAGIC_MODULO: return self.add_violation(MagicNumberViolation(node, text=str(node.n))) def visit_Num(self, node: ast.Num) -> None: """ Checks numbers not to be magic constants inside the code. Raises: MagicNumberViolation """ self._check_is_magic(node) self.generic_visit(node) PK!fO O 0wemake_python_styleguide/visitors/ast/classes.py# -*- coding: utf-8 -*- import ast from typing import ClassVar, FrozenSet from wemake_python_styleguide import constants, types from wemake_python_styleguide.logics.nodes import is_contained from wemake_python_styleguide.violations.best_practices import ( BadMagicMethodViolation, StaticMethodViolation, YieldInsideInitViolation, ) from wemake_python_styleguide.violations.complexity import ( TooManyBaseClassesViolation, ) from wemake_python_styleguide.violations.consistency import ( ObjectInBaseClassesListViolation, RequiredBaseClassViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor from wemake_python_styleguide.visitors.decorators import alias @types.final @alias('visit_any_function', ( 'visit_FunctionDef', 'visit_AsyncFunctionDef', )) class WrongClassVisitor(BaseNodeVisitor): """ This class is responsible for restricting some ``class`` anti-patterns. Here we check for stylistic issues and design patterns. """ _staticmethod_names: ClassVar[FrozenSet[str]] = frozenset(( 'staticmethod', )) _not_appropriate_for_init: ClassVar[types.AnyNodes] = ( ast.Yield, ) def _check_decorators(self, node: types.AnyFunctionDef) -> None: for decorator in node.decorator_list: decorator_name = getattr(decorator, 'id', None) if decorator_name in self._staticmethod_names: self.add_violation(StaticMethodViolation(node)) def _check_magic_methods(self, node: types.AnyFunctionDef) -> None: if node.name in constants.MAGIC_METHODS_BLACKLIST: self.add_violation(BadMagicMethodViolation(node, text=node.name)) def _check_base_classes(self, node: ast.ClassDef) -> None: """Check 'object' class in parent list.""" if len(node.bases) == 0: self.add_violation( RequiredBaseClassViolation(node, text=node.name), ) if len(node.bases) >= 2: for base_name in node.bases: id_attr = getattr(base_name, 'id', None) if id_attr == 'object': self.add_violation( ObjectInBaseClassesListViolation(node, text=id_attr), ) if len(node.bases) > self.options.max_base_classes: self.add_violation( TooManyBaseClassesViolation(node, text=node.name), ) def _check_method_contents(self, node: types.AnyFunctionDef) -> None: if node.name == constants.INIT: if is_contained(node, self._not_appropriate_for_init): self.add_violation(YieldInsideInitViolation(node)) def visit_ClassDef(self, node: ast.ClassDef) -> None: """ Checking class definitions. Raises: RequiredBaseClassViolation ObjectInBaseClassesListViolation """ self._check_base_classes(node) self.generic_visit(node) def visit_any_function(self, node: types.AnyFunctionDef) -> None: """ Checking class methods: async and regular. Raises: StaticMethodViolation BadMagicMethodViolation YieldInsideInitViolation """ self._check_decorators(node) self._check_magic_methods(node) self._check_method_contents(node) self.generic_visit(node) PK!hI4wemake_python_styleguide/visitors/ast/comparisons.py# -*- coding: utf-8 -*- import ast from typing import ClassVar, Sequence from wemake_python_styleguide.logics.nodes import is_literal from wemake_python_styleguide.logics.variables.name_nodes import ( is_same_variable, ) from wemake_python_styleguide.types import AnyIf, AnyNodes, final from wemake_python_styleguide.violations.consistency import ( ComparisonOrderViolation, ConstantComparisonViolation, MultipleInComparisonViolation, RedundantComparisonViolation, WrongConditionalViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor from wemake_python_styleguide.visitors.decorators import alias @final class ComparisonSanityVisitor(BaseNodeVisitor): """Restricts the comparison of literals.""" def _has_multiple_in_comparisons(self, node: ast.Compare) -> bool: count = 0 for op in node.ops: if isinstance(op, ast.In): count += 1 return count > 1 def _check_literal_compare(self, node: ast.Compare) -> None: last_was_literal = is_literal(node.left) for comparator in node.comparators: next_is_literal = is_literal(comparator) if last_was_literal and next_is_literal: self.add_violation(ConstantComparisonViolation(node)) break last_was_literal = next_is_literal def _check_redundant_compare(self, node: ast.Compare) -> None: last_variable = node.left for next_variable in node.comparators: if is_same_variable(last_variable, next_variable): self.add_violation(RedundantComparisonViolation(node)) break last_variable = next_variable def _check_multiple_in_comparisons(self, node: ast.Compare) -> None: if self._has_multiple_in_comparisons(node): self.add_violation(MultipleInComparisonViolation(node)) def visit_Compare(self, node: ast.Compare) -> None: """ Ensures that compares are written correctly. Raises: ConstantComparisonViolation MultipleInComparisonViolation RedundantComparisonViolation """ self._check_literal_compare(node) self._check_redundant_compare(node) self._check_multiple_in_comparisons(node) self.generic_visit(node) @final class WrongComparisionOrderVisitor(BaseNodeVisitor): """Restricts comparision where argument doesn't come first.""" _allowed_left_nodes: ClassVar[AnyNodes] = ( ast.Name, ast.Call, ast.Attribute, ) _special_cases: ClassVar[AnyNodes] = ( ast.In, ast.NotIn, ) def _is_special_case(self, node: ast.Compare) -> bool: """ Operators ``in`` and ``not in`` are special cases. Why? Because it is perfectly fine to use something like: ``if 'key' in some_dict: ...`` This should not be an issue. When there are multiple special operators it is still a separate issue. """ return isinstance(node.ops[0], self._special_cases) def _is_left_node_valid(self, left: ast.AST) -> bool: if isinstance(left, self._allowed_left_nodes): return True if isinstance(left, ast.BinOp): return ( self._is_left_node_valid(left.left) or self._is_left_node_valid(left.right) ) return False def _has_wrong_nodes_on_the_right( self, comparators: Sequence[ast.AST], ) -> bool: for right in comparators: if isinstance(right, self._allowed_left_nodes): return True if isinstance(right, ast.BinOp): return self._has_wrong_nodes_on_the_right([ right.left, right.right, ]) return False def _check_ordering(self, node: ast.Compare) -> None: if self._is_left_node_valid(node.left): return if self._is_special_case(node): return if len(node.comparators) > 1: return if not self._has_wrong_nodes_on_the_right(node.comparators): return self.add_violation(ComparisonOrderViolation(node)) def visit_Compare(self, node: ast.Compare) -> None: """ Forbids comparision where argument doesn't come first. Raises: ComparisonOrderViolation """ self._check_ordering(node) self.generic_visit(node) @alias('visit_any_if', ( 'visit_If', 'visit_IfExp', )) class WrongConditionalVisitor(BaseNodeVisitor): """Finds wrong conditional arguments.""" _forbidden_nodes: ClassVar[AnyNodes] = ( ast.List, ast.Set, ast.Num, ast.NameConstant, ast.Str, ast.Dict, ) def visit_any_if(self, node: AnyIf) -> None: """ Ensures that if statements are using valid conditionals. Raises: WrongConditionalViolation """ self._check_if_statement_conditional(node) self.generic_visit(node) def _check_if_statement_conditional(self, node: AnyIf) -> None: if isinstance(node.test, self._forbidden_nodes): self.add_violation(WrongConditionalViolation(node)) PK!oqWW<wemake_python_styleguide/visitors/ast/complexity/__init__.py# -*- coding: utf-8 -*- # TODO: refactor "complexity" visitors to be regular visitors PK!nё:wemake_python_styleguide/visitors/ast/complexity/counts.py# -*- coding: utf-8 -*- import ast from collections import defaultdict from typing import DefaultDict, Union from wemake_python_styleguide.logics.functions import is_method from wemake_python_styleguide.types import AnyFunctionDef, AnyImport, final from wemake_python_styleguide.violations.complexity import ( TooManyConditionsViolation, TooManyImportsViolation, TooManyMethodsViolation, TooManyModuleMembersViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor from wemake_python_styleguide.visitors.decorators import alias ConditionNodes = Union[ast.If, ast.While, ast.IfExp] ModuleMembers = Union[ast.AsyncFunctionDef, ast.FunctionDef, ast.ClassDef] @final @alias('visit_module_members', ( 'visit_ClassDef', 'visit_AsyncFunctionDef', 'visit_FunctionDef', )) class ModuleMembersVisitor(BaseNodeVisitor): """Counts classes and functions in a module.""" def __init__(self, *args, **kwargs) -> None: """Creates a counter for tracked metrics.""" super().__init__(*args, **kwargs) self._public_items_count = 0 def _check_members_count(self, node: ModuleMembers) -> None: """This method increases the number of module members.""" parent = getattr(node, 'parent', None) is_real_method = is_method(getattr(node, 'function_type', None)) if isinstance(parent, ast.Module) and not is_real_method: self._public_items_count += 1 def _post_visit(self) -> None: if self._public_items_count > self.options.max_module_members: self.add_violation(TooManyModuleMembersViolation()) def visit_module_members(self, node: ModuleMembers) -> None: """ Counts the number of ModuleMembers in a single module. Raises: TooManyModuleMembersViolation """ self._check_members_count(node) self.generic_visit(node) @final @alias('visit_any_import', ( 'visit_ImportFrom', 'visit_Import', )) class ImportMembersVisitor(BaseNodeVisitor): """Counts imports in a module.""" def __init__(self, *args, **kwargs) -> None: """Creates a counter for tracked metrics.""" super().__init__(*args, **kwargs) self._imports_count = 0 def _post_visit(self) -> None: if self._imports_count > self.options.max_imports: self.add_violation( TooManyImportsViolation(text=str(self._imports_count)), ) def visit_any_import(self, node: AnyImport) -> None: """ Counts the number of ``import`` and ``from ... import ...``. Raises: TooManyImportsViolation """ self._imports_count += 1 self.generic_visit(node) @final @alias('visit_any_function', ( 'visit_FunctionDef', 'visit_AsyncFunctionDef', )) class MethodMembersVisitor(BaseNodeVisitor): """Counts methods in a single class.""" def __init__(self, *args, **kwargs) -> None: """Creates a counter for tracked methods in different classes.""" super().__init__(*args, **kwargs) self._methods: DefaultDict[ast.ClassDef, int] = defaultdict(int) def _check_method(self, node: AnyFunctionDef) -> None: parent = getattr(node, 'parent', None) if isinstance(parent, ast.ClassDef): self._methods[parent] += 1 def _post_visit(self) -> None: for node, count in self._methods.items(): if count > self.options.max_methods: self.add_violation( TooManyMethodsViolation(node, text=node.name), ) def visit_any_function(self, node: AnyFunctionDef) -> None: """ Counts the number of methods in a single class. Raises: TooManyMethodsViolation """ self._check_method(node) self.generic_visit(node) @final @alias('visit_condition', ( 'visit_While', 'visit_IfExp', 'visit_If', )) class ConditionsVisitor(BaseNodeVisitor): """Checks ``if`` and ``while`` statements for condition counts.""" def __init__(self, *args, **kwargs) -> None: """Creates a counter for tracked conditions.""" super().__init__(*args, **kwargs) self._conditions: DefaultDict[ast.AST, int] = defaultdict(int) def _check_conditions(self, node: ast.AST) -> None: for condition in ast.walk(node): if isinstance(condition, (ast.And, ast.Or)): self._conditions[node] += 1 def _post_visit(self) -> None: for node, count in self._conditions.items(): if count > self.options.max_conditions - 1: self.add_violation( TooManyConditionsViolation(node, text=str(count)), ) def visit_comprehension(self, node: ast.comprehension) -> None: """ Counts the number of conditions in list comprehensions. Raises: TooManyConditionsViolation """ if node.ifs: # We only check the first `if`, since it is forbidden # to have more than one at a time # by `MultipleIfsInComprehensionViolation` self._check_conditions(node.ifs[0]) self.generic_visit(node) def visit_condition(self, node: ConditionNodes) -> None: """ Counts the number of conditions. Raises: TooManyConditionsViolation """ self._check_conditions(node.test) self.generic_visit(node) PK!ăpp<wemake_python_styleguide/visitors/ast/complexity/function.py# -*- coding: utf-8 -*- import ast from collections import defaultdict from typing import ClassVar, DefaultDict, List from wemake_python_styleguide.constants import UNUSED_VARIABLE from wemake_python_styleguide.logics.functions import is_method from wemake_python_styleguide.types import ( AnyFunctionDef, AnyFunctionDefAndLambda, AnyNodes, final, ) from wemake_python_styleguide.violations.complexity import ( TooManyArgumentsViolation, TooManyElifsViolation, TooManyExpressionsViolation, TooManyLocalsViolation, TooManyReturnsViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor from wemake_python_styleguide.visitors.decorators import alias FunctionCounter = DefaultDict[AnyFunctionDef, int] FunctionCounterWithLambda = DefaultDict[AnyFunctionDefAndLambda, int] @final class _ComplexityCounter(object): """Helper class to encapsulate logic from the visitor.""" _not_contain_locals: ClassVar[AnyNodes] = ( ast.comprehension, ) def __init__(self) -> None: self.arguments: FunctionCounterWithLambda = defaultdict(int) self.elifs: FunctionCounter = defaultdict(int) self.returns: FunctionCounter = defaultdict(int) self.expressions: FunctionCounter = defaultdict(int) self.variables: DefaultDict[ AnyFunctionDef, List[str], ] = defaultdict(list) def _update_variables( self, function: AnyFunctionDef, variable: ast.Name, ) -> None: """ Increases the counter of local variables. What is treated as a local variable? Check ``TooManyLocalsViolation`` documentation. """ function_variables = self.variables[function] if variable.id not in function_variables: if variable.id == UNUSED_VARIABLE: return parent = getattr(variable, 'parent', None) if isinstance(parent, self._not_contain_locals): return function_variables.append(variable.id) def _update_elifs(self, node: AnyFunctionDef, sub_node: ast.If) -> None: has_elif = any( isinstance(if_node, ast.If) for if_node in sub_node.orelse ) if has_elif: self.elifs[node] += 1 def _check_sub_node(self, node: AnyFunctionDef, sub_node) -> None: is_variable = isinstance(sub_node, ast.Name) context = getattr(sub_node, 'ctx', None) if is_variable and isinstance(context, ast.Store): self._update_variables(node, sub_node) elif isinstance(sub_node, ast.Return): self.returns[node] += 1 elif isinstance(sub_node, ast.Expr): self.expressions[node] += 1 elif isinstance(sub_node, ast.If): self._update_elifs(node, sub_node) def check_arguments_count(self, node: AnyFunctionDefAndLambda) -> None: """Checks the number of the arguments in a function.""" counter = 0 has_extra_arg = 0 if is_method(getattr(node, 'function_type', None)): has_extra_arg = 1 counter += len(node.args.args) + len(node.args.kwonlyargs) if node.args.vararg: counter += 1 if node.args.kwarg: counter += 1 self.arguments[node] = counter - has_extra_arg def check_function_complexity(self, node: AnyFunctionDef) -> None: """ In this function we iterate all the internal body's node. We check different complexity metrics based on these internals. """ for body_item in node.body: for sub_node in ast.walk(body_item): self._check_sub_node(node, sub_node) @final @alias('visit_any_function', ( 'visit_AsyncFunctionDef', 'visit_FunctionDef', )) class FunctionComplexityVisitor(BaseNodeVisitor): """ This class checks for complexity inside functions. This includes: 1. Number of arguments 2. Number of `return` statements 3. Number of expressions 4. Number of local variables 5. Number of `elif` branches """ def __init__(self, *args, **kwargs) -> None: """Creates a counter for tracked metrics.""" super().__init__(*args, **kwargs) self._counter = _ComplexityCounter() def _check_possible_switch(self) -> None: for node, elifs in self._counter.elifs.items(): if elifs > self.options.max_elifs: self.add_violation(TooManyElifsViolation(node)) def _check_function_internals(self) -> None: for node, variables in self._counter.variables.items(): if len(variables) > self.options.max_local_variables: self.add_violation( TooManyLocalsViolation(node, text=node.name), ) for node, expressions in self._counter.expressions.items(): if expressions > self.options.max_expressions: self.add_violation( TooManyExpressionsViolation(node, text=node.name), ) def _check_function_signature(self) -> None: for node, arguments in self._counter.arguments.items(): if arguments > self.options.max_arguments: self.add_violation( TooManyArgumentsViolation(node, text=str(arguments)), ) for node, returns in self._counter.returns.items(): if returns > self.options.max_returns: self.add_violation( TooManyReturnsViolation(node, text=node.name), ) def _post_visit(self) -> None: self._check_function_signature() self._check_function_internals() self._check_possible_switch() def visit_any_function(self, node: AnyFunctionDef) -> None: """ Checks function's internal complexity. Raises: TooManyExpressionsViolation TooManyReturnsViolation TooManyLocalsViolation TooManyArgumentsViolation TooManyElifsViolation """ self._counter.check_arguments_count(node) self._counter.check_function_complexity(node) self.generic_visit(node) def visit_Lambda(self, node: ast.Lambda) -> None: """ Checks lambda function's internal complexity. Raises: TooManyArgumentsViolation """ self._counter.check_arguments_count(node) self.generic_visit(node) PK!9 9wemake_python_styleguide/visitors/ast/complexity/jones.py# -*- coding: utf-8 -*- """ Jones Complexity to count inline complexity. Based on the original `jones-complexity` project: https://github.com/Miserlou/JonesComplexity Original project is licensed under MIT. """ import ast from collections import defaultdict from statistics import median from typing import DefaultDict, List from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.complexity import ( JonesScoreViolation, LineComplexityViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor @final class JonesComplexityVisitor(BaseNodeVisitor): """ This visitor is used to find complex lines in the code. Calculates the number of AST nodes per line of code. Also calculates the median nodes/line score. Then compares these numbers to the given tressholds. Some nodes are ignored because there's no sense in analyzing them. Some nodes like type annotations are not affecting line complexity, so we do not count them. """ _ignored_nodes = ( ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef, ) def __init__(self, *args, **kwargs) -> None: """Initializes line number counter.""" super().__init__(*args, **kwargs) self._lines: DefaultDict[int, List[ast.AST]] = defaultdict(list) self._to_ignore: List[ast.AST] = [] def _post_visit(self) -> None: """ Triggers after the whole module was processed. Checks each line for its complexity, compares it to the tresshold. We also calculate the final Jones score for the whole module. """ for line_nodes in self._lines.values(): complexity = len(line_nodes) if complexity > self.options.max_line_complexity: self.add_violation(LineComplexityViolation( line_nodes[0], text=str(complexity), )) node_counts = [len(nodes) for nodes in self._lines.values()] total_count = median(node_counts) if node_counts else 0 if total_count > self.options.max_jones_score: self.add_violation(JonesScoreViolation()) def _maybe_ignore_child(self, node: ast.AST) -> bool: if isinstance(node, ast.AnnAssign): self._to_ignore.append(node.annotation) return node in self._to_ignore def visit(self, node: ast.AST) -> None: """ Visits all nodes, sums the number of nodes per line. Then calculates the median value of all line results. Raises: JonesScoreViolation LineComplexityViolation """ line_number = getattr(node, 'lineno', None) is_ignored = isinstance(node, self._ignored_nodes) if line_number is not None and not is_ignored: if not self._maybe_ignore_child(node): self._lines[line_number].append(node) self.generic_visit(node) PK!v#V :wemake_python_styleguide/visitors/ast/complexity/nested.py# -*- coding: utf-8 -*- import ast from typing import ClassVar from wemake_python_styleguide.constants import ( NESTED_CLASSES_WHITELIST, NESTED_FUNCTIONS_WHITELIST, ) from wemake_python_styleguide.types import AnyFunctionDef, AnyNodes, final from wemake_python_styleguide.violations.best_practices import ( NestedClassViolation, NestedFunctionViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor from wemake_python_styleguide.visitors.decorators import alias @final @alias('visit_any_function', ( 'visit_FunctionDef', 'visit_AsyncFunctionDef', )) class NestedComplexityVisitor(BaseNodeVisitor): """ Checks that structures are not nested. We disallow to use nested functions and nested classes. Because flat is better than nested. We allow to nest function inside classes, that's called methods. """ _function_nodes: ClassVar[AnyNodes] = ( ast.FunctionDef, ast.AsyncFunctionDef, ) def _check_nested_function(self, node: AnyFunctionDef) -> None: parent = getattr(node, 'parent', None) is_inside_function = isinstance(parent, self._function_nodes) if is_inside_function and node.name not in NESTED_FUNCTIONS_WHITELIST: self.add_violation(NestedFunctionViolation(node, text=node.name)) def _check_nested_classes(self, node: ast.ClassDef) -> None: parent = getattr(node, 'parent', None) is_inside_class = isinstance(parent, ast.ClassDef) is_inside_function = isinstance(parent, self._function_nodes) if is_inside_class and node.name not in NESTED_CLASSES_WHITELIST: self.add_violation(NestedClassViolation(node, text=node.name)) elif is_inside_function: self.add_violation(NestedClassViolation(node, text=node.name)) def _check_nested_lambdas(self, node: ast.Lambda) -> None: parent = getattr(node, 'parent', None) if isinstance(parent, ast.Lambda): self.add_violation(NestedFunctionViolation(node)) def visit_ClassDef(self, node: ast.ClassDef) -> None: """ Used to find nested classes in other classes and functions. Uses ``NESTED_CLASSES_WHITELIST`` to respect some nested classes. Raises: NestedClassViolation """ self._check_nested_classes(node) self.generic_visit(node) def visit_any_function(self, node: AnyFunctionDef) -> None: """ Used to find nested functions. Uses ``NESTED_FUNCTIONS_WHITELIST`` to respect some nested functions. Raises: NestedFunctionViolation """ self._check_nested_function(node) self.generic_visit(node) def visit_Lambda(self, node: ast.Lambda) -> None: """ Used to find nested ``lambda`` functions. Raises: NestedFunctionViolation """ self._check_nested_lambdas(node) self.generic_visit(node) PK!Sa :wemake_python_styleguide/visitors/ast/complexity/offset.py# -*- coding: utf-8 -*- import ast from typing import Union from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.complexity import ( TooDeepNestingViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor from wemake_python_styleguide.visitors.decorators import alias AsyncNodes = Union[ast.AsyncFunctionDef, ast.AsyncFor, ast.AsyncWith] @final @alias('visit_line_expression', ( 'visit_Try', 'visit_ExceptHandler', 'visit_Expr', 'visit_For', 'visit_With', 'visit_While', 'visit_If', 'visit_Raise', 'visit_Return', 'visit_Continue', 'visit_Break', 'visit_Assign', 'visit_Expr', 'visit_Await', 'visit_Pass', )) @alias('visit_async_statement', ( 'visit_AsyncFor', 'visit_AsyncWith', 'visit_AsyncFunctionDef', )) class OffsetVisitor(BaseNodeVisitor): """Checks offset values for several nodes.""" def _check_offset(self, node: ast.AST, error: int = 0) -> None: offset = getattr(node, 'col_offset', 0) - error if offset > self.options.max_offset_blocks * 4: self.add_violation(TooDeepNestingViolation(node)) def visit_line_expression(self, node: ast.AST) -> None: """ Checks statement's offset. We check only several nodes, because other nodes might have different offsets, which is fine. For example, ``ast.Name`` node has inline offset, which can take values from ``0`` to ``~80``. But ``Name`` node is allowed to behave like so. So, we only check nodes that represent "all liners". Raises: TooDeepNestingViolation """ self._check_offset(node) self.generic_visit(node) def visit_async_statement(self, node: AsyncNodes): """ Checks async definitions offset. This is a temporary check for async-based expressions, because offset for them isn't calculated properly. We can calculate right version of offset with subscripting ``6`` (length of "async " part). Read more: https://bugs.python.org/issue29205 github.com/wemake-services/wemake-python-styleguide/issues/282 Raises: TooDeepNestingViolation """ error = 6 if node.col_offset % 4 != 0 else 0 self._check_offset(node, error=error) self.generic_visit(node) PK!2wemake_python_styleguide/visitors/ast/functions.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.constants import FUNCTIONS_BLACKLIST from wemake_python_styleguide.logics.functions import given_function_called from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.best_practices import ( WrongFunctionCallViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor @final class WrongFunctionCallVisitor(BaseNodeVisitor): """ Responsible for restricting some dangerous function calls. All these functions are defined in ``FUNCTIONS_BLACKLIST``. """ def visit_Call(self, node: ast.Call) -> None: """ Used to find ``FUNCTIONS_BLACKLIST`` calls. Raises: WrongFunctionCallViolation """ function_name = given_function_called(node, FUNCTIONS_BLACKLIST) if function_name: self.add_violation(WrongFunctionCallViolation( node, text=function_name, )) self.generic_visit(node) PK!f>Ɯ0wemake_python_styleguide/visitors/ast/imports.py# -*- coding: utf-8 -*- import ast from itertools import chain from typing import Callable from wemake_python_styleguide.constants import FUTURE_IMPORTS_WHITELIST from wemake_python_styleguide.logics import imports from wemake_python_styleguide.logics.variables import access from wemake_python_styleguide.types import AnyImport, final from wemake_python_styleguide.violations.base import BaseViolation from wemake_python_styleguide.violations.best_practices import ( FutureImportViolation, NestedImportViolation, ProtectedModuleViolation, ) from wemake_python_styleguide.violations.consistency import ( DottedRawImportViolation, LocalFolderImportViolation, ) from wemake_python_styleguide.violations.naming import SameAliasImportViolation from wemake_python_styleguide.visitors.base import BaseNodeVisitor ErrorCallback = Callable[[BaseViolation], None] @final class _ImportsChecker(object): """Utility class to separate logic from the visitor.""" def __init__(self, error_callback: ErrorCallback) -> None: self.error_callback = error_callback def check_nested_import(self, node: AnyImport) -> None: text = imports.get_error_text(node) parent = getattr(node, 'parent', None) if parent is not None and not isinstance(parent, ast.Module): self.error_callback(NestedImportViolation(node, text=text)) def check_local_import(self, node: ast.ImportFrom) -> None: text = imports.get_error_text(node) if node.level != 0: self.error_callback( LocalFolderImportViolation(node, text=text), ) def check_future_import(self, node: ast.ImportFrom) -> None: if node.module == '__future__': for alias in node.names: if alias.name not in FUTURE_IMPORTS_WHITELIST: self.error_callback( FutureImportViolation(node, text=alias.name), ) def check_dotted_raw_import(self, node: ast.Import) -> None: for alias in node.names: if '.' in alias.name: self.error_callback( DottedRawImportViolation(node, text=alias.name), ) def check_alias(self, node: AnyImport) -> None: for alias in node.names: if alias.asname == alias.name: self.error_callback( SameAliasImportViolation(node, text=alias.name), ) def check_protected_import(self, node: AnyImport) -> None: text = imports.get_error_text(node) import_names = [alias.name for alias in node.names] for name in chain(imports.get_import_parts(node), import_names): if access.is_protected_variable(name): self.error_callback( ProtectedModuleViolation(node, text=text), ) @final class WrongImportVisitor(BaseNodeVisitor): """Responsible for finding wrong imports.""" def __init__(self, *args, **kwargs) -> None: """Creates a checker for tracked violations.""" super().__init__(*args, **kwargs) self._checker = _ImportsChecker(self.add_violation) def visit_Import(self, node: ast.Import) -> None: """ Used to find wrong ``import`` statements. Raises: SameAliasImportViolation DottedRawImportViolation NestedImportViolation """ self._checker.check_nested_import(node) self._checker.check_dotted_raw_import(node) self._checker.check_alias(node) self._checker.check_protected_import(node) self.generic_visit(node) def visit_ImportFrom(self, node: ast.ImportFrom) -> None: """ Used to find wrong ``from ... import ...`` statements. Raises: SameAliasImportViolation NestedImportViolation LocalFolderImportViolation FutureImportViolation """ self._checker.check_local_import(node) self._checker.check_nested_import(node) self._checker.check_future_import(node) self._checker.check_alias(node) self._checker.check_protected_import(node) self.generic_visit(node) PK!1ee1wemake_python_styleguide/visitors/ast/keywords.py# -*- coding: utf-8 -*- import ast from collections import defaultdict from typing import ClassVar, DefaultDict, Optional, Union from wemake_python_styleguide.types import AnyNodes, final from wemake_python_styleguide.violations.best_practices import ( BaseExceptionViolation, RaiseNotImplementedViolation, RedundantFinallyViolation, RedundantForElseViolation, WrongKeywordViolation, ) from wemake_python_styleguide.violations.complexity import ( TooManyForsInComprehensionViolation, ) from wemake_python_styleguide.violations.consistency import ( MultipleIfsInComprehensionViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor AnyLoop = Union[ast.For, ast.While] @final class _ComprehensionComplexityCounter(object): """Helper class to encapsulate logic from the visitor.""" def __init__(self) -> None: self.fors: DefaultDict[ast.ListComp, int] = defaultdict(int) def check_fors(self, node: ast.comprehension) -> None: parent = getattr(node, 'parent', node) if isinstance(parent, ast.ListComp): self.fors[parent] = len(parent.generators) @final class WrongRaiseVisitor(BaseNodeVisitor): """Finds wrong ``raise`` keywords.""" def _check_exception_type(self, node: ast.Raise) -> None: exception = getattr(node, 'exc', None) if exception is None: return exception_func = getattr(exception, 'func', None) if exception_func: exception = exception_func exception_name = getattr(exception, 'id', None) if exception_name == 'NotImplemented': self.add_violation(RaiseNotImplementedViolation(node)) def visit_Raise(self, node: ast.Raise) -> None: """ Checks how ``raise`` keyword is used. Raises: RaiseNotImplementedViolation """ self._check_exception_type(node) self.generic_visit(node) @final class WrongKeywordVisitor(BaseNodeVisitor): """Finds wrong keywords.""" _forbidden_keywords: ClassVar[AnyNodes] = ( ast.Pass, ast.Delete, ast.Global, ast.Nonlocal, ) def _check_keyword(self, node: ast.AST) -> None: if isinstance(node, self._forbidden_keywords): self.add_violation(WrongKeywordViolation(node)) def visit(self, node: ast.AST) -> None: """ Used to find wrong keywords. Raises: WrongKeywordViolation """ self._check_keyword(node) self.generic_visit(node) @final class WrongListComprehensionVisitor(BaseNodeVisitor): """Checks list comprehensions.""" def __init__(self, *args, **kwargs) -> None: """Creates a counter for tracked metrics.""" super().__init__(*args, **kwargs) self._counter = _ComprehensionComplexityCounter() def _check_ifs(self, node: ast.comprehension) -> None: if len(node.ifs) > 1: # We are trying to fix line number in the report, # since `comprehension` does not have this property. parent = getattr(node, 'parent', node) self.add_violation(MultipleIfsInComprehensionViolation(parent)) def _check_fors(self) -> None: for node, for_count in self._counter.fors.items(): if for_count > 2: self.add_violation(TooManyForsInComprehensionViolation(node)) def _post_visit(self) -> None: self._check_fors() def visit_comprehension(self, node: ast.comprehension) -> None: """ Finds multiple ``if`` and ``for`` nodes inside the comprehension. Raises: MultipleIfsInComprehensionViolation, TooManyForsInComprehensionViolation, """ self._check_ifs(node) self._counter.check_fors(node) self.generic_visit(node) @final class WrongForElseVisitor(BaseNodeVisitor): """Responsible for restricting `else` in `for` loops without `break`.""" def _does_loop_contain_node( self, loop: Optional[AnyLoop], to_check: ast.Break, ) -> bool: if loop is None: return False for inner_node in ast.walk(loop): if to_check is inner_node: return True return False def _has_break(self, node: ast.For) -> bool: closest_loop = None for subnode in ast.walk(node): if isinstance(subnode, (ast.For, ast.While)): if subnode is not node: closest_loop = subnode if isinstance(subnode, ast.Break): is_nested_break = self._does_loop_contain_node( closest_loop, subnode, ) if not is_nested_break: return True return False def _check_for_needs_else(self, node: ast.For) -> None: if node.orelse and not self._has_break(node): self.add_violation(RedundantForElseViolation(node=node)) def visit_For(self, node: ast.For) -> None: """Used for find `else` block in `for` loops without `break`.""" self._check_for_needs_else(node) self.generic_visit(node) @final class WrongTryFinallyVisitor(BaseNodeVisitor): """Responsible for restricting finally in try blocks without except.""" def _check_for_needs_except(self, node: ast.Try) -> None: if node.finalbody and not node.handlers: self.add_violation(RedundantFinallyViolation(node=node)) def visit_Try(self, node: ast.Try) -> None: """Used for find finally in try blocks without except.""" self._check_for_needs_except(node) self.generic_visit(node) @final class WrongExceptionTypeVisitor(BaseNodeVisitor): """Finds usage of incorrect ``except`` exception types.""" _base_exception: ClassVar[str] = 'BaseException' def _check_exception_type(self, node: ast.ExceptHandler) -> None: exception_name = getattr(node, 'type', None) if exception_name is None: return exception_id = getattr(exception_name, 'id', None) if exception_id == self._base_exception: self.add_violation(BaseExceptionViolation(node)) def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None: """ Checks all ``ExceptionHandler`` nodes. Raises: BaseExceptionViolation """ self._check_exception_type(node) self.generic_visit(node) PK!qO^aa0wemake_python_styleguide/visitors/ast/modules.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.constants import INIT from wemake_python_styleguide.logics.filenames import is_stem_in_list from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.best_practices import ( EmptyModuleViolation, InitModuleHasLogicViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor @final class EmptyModuleContentsVisitor(BaseNodeVisitor): """Restricts to have empty modules.""" def _is_init(self) -> bool: return is_stem_in_list(self.filename, [INIT]) def _is_doc_string(self, node: ast.stmt) -> bool: """ Tells whether or not the given node is a docstring. We call docstrings any string nodes that are placed right after function, class, or module definition. """ if not isinstance(node, ast.Expr): return False return isinstance(node.value, ast.Str) def _check_module_contents(self, node: ast.Module) -> None: if self._is_init(): return if not node.body: self.add_violation(EmptyModuleViolation(node)) def _check_init_contents(self, node: ast.Module) -> None: if not self._is_init() or not node.body: return if not self.options.i_control_code: return if len(node.body) > 1: self.add_violation(InitModuleHasLogicViolation(node)) return if not self._is_doc_string(node.body[0]): self.add_violation(InitModuleHasLogicViolation(node)) def visit_Module(self, node: ast.Module) -> None: """ Checks that module has something other than module definition. We have completely different rules for ``__init__.py`` and regular files. Since, we believe that ``__init__.py`` must be empty. But, other files must have contents. Raises: EmptyModuleViolation InitModuleHasLogicViolation """ self._check_init_contents(node) self._check_module_contents(node) self.generic_visit(node) PK!G1/wemake_python_styleguide/visitors/ast/naming.py# -*- coding: utf-8 -*- import ast from typing import List, Tuple, Union from wemake_python_styleguide.constants import ( MODULE_METADATA_VARIABLES_BLACKLIST, VARIABLE_NAMES_BLACKLIST, ) from wemake_python_styleguide.logics.variables import access from wemake_python_styleguide.logics.variables.name_nodes import ( get_assigned_name, ) from wemake_python_styleguide.logics.variables.naming import ( is_too_short_variable_name, is_upper_case_name, is_variable_name_contains_consecutive_underscores, is_variable_name_with_underscored_number, is_wrong_variable_name, ) from wemake_python_styleguide.types import AnyFunctionDef, AnyImport, final from wemake_python_styleguide.violations.best_practices import ( ReassigningVariableToItselfViolation, WrongModuleMetadataViolation, ) from wemake_python_styleguide.violations.naming import ( ConsecutiveUnderscoresInNameViolation, PrivateNameViolation, TooShortVariableNameViolation, UnderScoredNumberNameViolation, UpperCaseAttributeViolation, WrongVariableNameViolation, ) from wemake_python_styleguide.visitors.base import BaseNodeVisitor from wemake_python_styleguide.visitors.decorators import alias VariableDef = Union[ast.Name, ast.Attribute, ast.ExceptHandler] AssignTargets = List[ast.expr] AssignTargetsNameList = List[Union[str, Tuple[str]]] @final @alias('visit_any_import', ( 'visit_ImportFrom', 'visit_Import', )) @alias('visit_any_function', ( 'visit_FunctionDef', 'visit_AsyncFunctionDef', )) @alias('visit_variable', ( 'visit_Name', 'visit_Attribute', 'visit_ExceptHandler', )) class WrongNameVisitor(BaseNodeVisitor): """Performs checks based on variable names.""" def _check_name(self, node: ast.AST, name: str) -> None: if is_wrong_variable_name(name, VARIABLE_NAMES_BLACKLIST): self.add_violation(WrongVariableNameViolation(node, text=name)) min_length = self.options.min_variable_length if is_too_short_variable_name(name, min_length=min_length): self.add_violation(TooShortVariableNameViolation(node, text=name)) if access.is_private_variable(name): self.add_violation(PrivateNameViolation(node, text=name)) if is_variable_name_with_underscored_number(name): self.add_violation(UnderScoredNumberNameViolation()) if is_variable_name_contains_consecutive_underscores(name): self.add_violation( ConsecutiveUnderscoresInNameViolation(node, text=name), ) def _check_function_signature(self, node: AnyFunctionDef) -> None: for arg in node.args.args: self._check_name(node, arg.arg) for arg in node.args.kwonlyargs: self._check_name(node, arg.arg) if node.args.vararg: self._check_name(node, node.args.vararg.arg) if node.args.kwarg: self._check_name(node, node.args.kwarg.arg) def _check_attribute_name(self, node: ast.ClassDef) -> None: top_level_assigns = [ sub_node for sub_node in node.body if isinstance(sub_node, ast.Assign) ] for assignment in top_level_assigns: for target in assignment.targets: name = getattr(target, 'id', None) if is_upper_case_name(name): self.add_violation( UpperCaseAttributeViolation(target, text=name), ) def visit_ClassDef(self, node: ast.ClassDef) -> None: """ Used to find upper attribute declarations. Raises: UpperCaseAttributeViolation """ self._check_attribute_name(node) self.generic_visit(node) def visit_any_function(self, node: AnyFunctionDef) -> None: """ Used to find wrong function and method parameters. Raises: WrongVariableNameViolation TooShortVariableNameViolation PrivateNameViolation """ self._check_name(node, node.name) self._check_function_signature(node) self.generic_visit(node) def visit_any_import(self, node: AnyImport) -> None: """ Used to check wrong import alias names. Raises: WrongVariableNameViolation TooShortVariableNameViolation PrivateNameViolation """ for alias_node in node.names: if alias_node.asname: self._check_name(node, alias_node.asname) self.generic_visit(node) def visit_variable(self, node: VariableDef) -> None: """ Used to check wrong names of assigned. Raises: WrongVariableNameViolation TooShortVariableNameViolation PrivateNameViolation """ variable_name = get_assigned_name(node) if variable_name is not None: self._check_name(node, variable_name) self.generic_visit(node) @final class WrongModuleMetadataVisitor(BaseNodeVisitor): """Finds wrong metadata information of a module.""" def _check_metadata(self, node: ast.Assign) -> None: node_parent = getattr(node, 'parent', None) if not isinstance(node_parent, ast.Module): return for target_node in node.targets: target_node_id = getattr(target_node, 'id', None) if target_node_id in MODULE_METADATA_VARIABLES_BLACKLIST: self.add_violation( WrongModuleMetadataViolation(node, text=target_node_id), ) def visit_Assign(self, node: ast.Assign) -> None: """ Used to find the bad metadata variable names. Raises: WrongModuleMetadataViolation """ self._check_metadata(node) self.generic_visit(node) @final class WrongVariableAssignmentVisitor(BaseNodeVisitor): """Finds wrong variables assignments.""" def _create_target_names( self, target: AssignTargets, ) -> AssignTargetsNameList: """Creates list with names of targets of assignment.""" target_names = [] for ast_object in target: if isinstance(ast_object, ast.Name): target_names.append(getattr(ast_object, 'id', None)) if isinstance(ast_object, ast.Tuple): target_names.append(getattr(ast_object, 'elts', None)) for index, _ in enumerate(target_names): target_names[index] = tuple( name.id for name in target_names[index] ) return target_names def _check_assignment(self, node: ast.Assign) -> None: target_names = self._create_target_names(node.targets) if isinstance(node.value, ast.Tuple): node_values = node.value.elts values_names = tuple( getattr(node_value, 'id', None) for node_value in node_values ) else: values_names = getattr(node.value, 'id', None) has_repeatable_values = len(target_names) != len(set(target_names)) if values_names in target_names or has_repeatable_values: self.add_violation(ReassigningVariableToItselfViolation(node)) def visit_Assign(self, node: ast.Assign) -> None: """ Used to check assignment variable to itself. Raises: ReassigningVariableToItselfViolation """ self._check_assignment(node) self.generic_visit(node) PK!wyy)wemake_python_styleguide/visitors/base.py# -*- coding: utf-8 -*- """ Contains detailed documentation about how to write a visitor. .. _visitors: Creating new visitor -------------------- First of all, you have to decide what base class do you want to use? .. currentmodule:: wemake_python_styleguide.visitors.base Available base classes ~~~~~~~~~~~~~~~~~~~~~~ .. autosummary:: :nosignatures: BaseNodeVisitor BaseFilenameVisitor BaseTokenVisitor The decision relies on what parameters do you need for the task. It is highly unlikely that you will need two parameters at the same time. Visitors API ------------ """ import ast import tokenize from typing import List, Sequence, Type from wemake_python_styleguide import constants from wemake_python_styleguide.types import ConfigurationOptions, final from wemake_python_styleguide.violations.base import BaseViolation class BaseVisitor(object): """ Abstract base class for different types of visitors. Attributes: options: contains the options objects passed and parsed by ``flake8``. filename: filename passed by ``flake8``, each visitor has a file name. violations: list of violations for the specific visitor. """ def __init__( self, options: ConfigurationOptions, filename: str = constants.STDIN, ) -> None: """Creates base visitor instance.""" self.options = options self.filename = filename self.violations: List[BaseViolation] = [] @classmethod def from_checker(cls: Type['BaseVisitor'], checker) -> 'BaseVisitor': """ Constructs visitor instance from the checker. Each unique visitor class should know how to construct itself from the ``checker`` instance. Generally speaking, each visitor class needs to eject required parameters from checker and then run its constructor with these parameters. """ return cls(options=checker.options, filename=checker.filename) @final def add_violation(self, violation: BaseViolation) -> None: """Adds violation to the visitor.""" self.violations.append(violation) def run(self) -> None: """ Abstract method to run a visitor. Each visitor should know what exactly it needs to do when it was told to ``run``. This method should be defined in all subclasses. """ raise NotImplementedError('Should be defined in a subclass') class BaseNodeVisitor(ast.NodeVisitor, BaseVisitor): """ Allows to store violations while traversing node tree. This class should be used as a base class for all ``ast`` based checkers. Method ``visit()`` is defined in ``NodeVisitor`` class. Attributes: tree: ``ast`` tree to be checked. """ def __init__( self, options: ConfigurationOptions, tree: ast.AST, **kwargs, ) -> None: """Creates new ``ast`` based instance.""" super().__init__(options, **kwargs) self.tree = tree @final @classmethod def from_checker( cls: Type['BaseNodeVisitor'], checker, ) -> 'BaseNodeVisitor': """Constructs visitor instance from the checker.""" return cls( options=checker.options, filename=checker.filename, tree=checker.tree, ) def _post_visit(self) -> None: """ Executed after all nodes have been visited. This method is useful for counting statistics, etc. By default does nothing. """ @final def run(self) -> None: """Recursively visits all ``ast`` nodes. Then executes post hook.""" self.visit(self.tree) self._post_visit() class BaseFilenameVisitor(BaseVisitor): """ Abstract base class that allows to visit and check module file names. Has ``visit_filename()`` method that should be defined in subclasses. """ def visit_filename(self) -> None: """ Abstract method to check module file names. This method should be overridden in a subclass. """ raise NotImplementedError('Should be defined in a subclass') @final def run(self) -> None: """ Checks module's filename. Skips modules that are checked as piped output. Since these modules are checked as a ``stdin`` input. And do not have names. """ if self.filename != constants.STDIN: self.visit_filename() class BaseTokenVisitor(BaseVisitor): """ Allows to check ``tokenize`` sequences. Attributes: file_tokens: ``tokenize.TokenInfo`` sequence to be checked. """ def __init__( self, options: ConfigurationOptions, file_tokens: Sequence[tokenize.TokenInfo], **kwargs, ) -> None: """Creates new ``tokenize`` based visitor instance.""" super().__init__(options, **kwargs) self.file_tokens = file_tokens @final @classmethod def from_checker( cls: Type['BaseTokenVisitor'], checker, ) -> 'BaseTokenVisitor': """Constructs ``tokenize`` based visitor instance from the checker.""" return cls( options=checker.options, filename=checker.filename, file_tokens=checker.file_tokens, ) def visit(self, token: tokenize.TokenInfo) -> None: """ Runs custom defined handlers in a visitor for each specific token type. Uses ``.exact_type`` property to fetch the token name. So, you have to be extra careful with tokens like ``->`` and other operators, since they might resolve in just ``OP`` name. Does nothing if handler for any token type is not defined. See also: https://docs.python.org/3/library/tokenize.html """ token_type = tokenize.tok_name[token.exact_type].lower() method = getattr(self, 'visit_' + token_type, None) if method is not None: method(token) @final def run(self) -> None: """Visits all token types that have a handler method.""" for token in self.file_tokens: self.visit(token) PK!|QR/wemake_python_styleguide/visitors/decorators.py# -*- coding: utf-8 -*- from typing import Callable, Iterable def alias( original: str, aliases: Iterable[str], ) -> Callable[[type], type]: """ Decorator to alias handlers. Why do we need it? Because there are cases when we need to use the same method to handle different nodes types. We can just create aliases like ``visit_Import = visit_ImportFrom``, but it looks verbose and ugly. """ def decorator(cls: type) -> type: original_handler = getattr(cls, original) for alias in aliases: setattr(cls, alias, original_handler) return cls return decorator PK!uh7wemake_python_styleguide/visitors/filenames/__init__.py# -*- coding: utf-8 -*- PK!f @wemake_python_styleguide/visitors/filenames/wrong_module_name.py# -*- coding: utf-8 -*- from wemake_python_styleguide import constants from wemake_python_styleguide.logics import filenames from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.naming import ( TooShortModuleNameViolation, UnderScoredNumberNameViolation, WrongModuleMagicNameViolation, WrongModuleNamePatternViolation, WrongModuleNameUnderscoresViolation, WrongModuleNameViolation, ) from wemake_python_styleguide.visitors.base import BaseFilenameVisitor @final class WrongModuleNameVisitor(BaseFilenameVisitor): """Checks that modules have correct names.""" def _check_module_name(self) -> None: is_wrong_name = filenames.is_stem_in_list( self.filename, constants.MODULE_NAMES_BLACKLIST, ) if is_wrong_name: self.add_violation(WrongModuleNameViolation()) def _check_magic_name(self) -> None: if filenames.is_magic(self.filename): good_magic = filenames.is_stem_in_list( self.filename, constants.MAGIC_MODULE_NAMES_WHITELIST, ) if not good_magic: self.add_violation(WrongModuleMagicNameViolation()) def _check_module_name_length(self) -> None: is_short = filenames.is_too_short_stem( self.filename, min_length=self.options.min_module_name_length, ) if is_short: self.add_violation(TooShortModuleNameViolation()) def _check_module_name_pattern(self) -> None: if not filenames.is_matching_pattern(self.filename): self.add_violation(WrongModuleNamePatternViolation()) def _check_underscores(self) -> None: repeating_underscores = self.filename.count('__') if filenames.is_magic(self.filename): repeating_underscores -= 2 if repeating_underscores > 0: self.add_violation(WrongModuleNameUnderscoresViolation()) def _check_underscored_numbers(self) -> None: if not filenames.is_stem_with_underscored_number(self.filename): self.add_violation(UnderScoredNumberNameViolation()) def visit_filename(self) -> None: """ Checks a single module's filename. Raises: TooShortModuleNameViolation WrongModuleMagicNameViolation WrongModuleNameViolation WrongModuleNamePatternViolation WrongModuleNameUnderscoresViolation UnderScoredNumberNameViolation """ self._check_module_name() self._check_magic_name() self._check_module_name_length() self._check_module_name_pattern() self._check_underscores() self._check_underscored_numbers() PK!uh5wemake_python_styleguide/visitors/presets/__init__.py# -*- coding: utf-8 -*- PK!cys  7wemake_python_styleguide/visitors/presets/complexity.py# -*- coding: utf-8 -*- from wemake_python_styleguide.visitors.ast.complexity import ( counts, function, jones, nested, offset, ) #: Used to store all complexity related visitors to be later passed to checker: COMPLEXITY_PRESET = ( function.FunctionComplexityVisitor, jones.JonesComplexityVisitor, nested.NestedComplexityVisitor, offset.OffsetVisitor, counts.ImportMembersVisitor, counts.ModuleMembersVisitor, counts.MethodMembersVisitor, counts.ConditionsVisitor, ) PK!4wemake_python_styleguide/visitors/presets/general.py# -*- coding: utf-8 -*- from wemake_python_styleguide.visitors.ast import ( attributes, builtins, comparisons, keywords, naming, ) from wemake_python_styleguide.visitors.ast.classes import WrongClassVisitor from wemake_python_styleguide.visitors.ast.functions import ( WrongFunctionCallVisitor, ) from wemake_python_styleguide.visitors.ast.imports import WrongImportVisitor from wemake_python_styleguide.visitors.ast.modules import ( EmptyModuleContentsVisitor, ) from wemake_python_styleguide.visitors.filenames.wrong_module_name import ( WrongModuleNameVisitor, ) #: Used to store all general visitors to be later passed to checker: GENERAL_PRESET = ( # General: keywords.WrongRaiseVisitor, keywords.WrongKeywordVisitor, keywords.WrongListComprehensionVisitor, keywords.WrongForElseVisitor, keywords.WrongTryFinallyVisitor, keywords.WrongExceptionTypeVisitor, attributes.WrongAttributeVisitor, WrongFunctionCallVisitor, WrongImportVisitor, naming.WrongNameVisitor, naming.WrongModuleMetadataVisitor, naming.WrongVariableAssignmentVisitor, builtins.MagicNumberVisitor, builtins.WrongStringVisitor, comparisons.WrongConditionalVisitor, comparisons.ComparisonSanityVisitor, comparisons.WrongComparisionOrderVisitor, # Classes: WrongClassVisitor, # Modules: WrongModuleNameVisitor, EmptyModuleContentsVisitor, ) PK!j3wemake_python_styleguide/visitors/presets/tokens.py# -*- coding: utf-8 -*- from wemake_python_styleguide.visitors.tokenize.comments import ( WrongCommentVisitor, ) from wemake_python_styleguide.visitors.tokenize.keywords import ( WrongKeywordTokenVisitor, ) from wemake_python_styleguide.visitors.tokenize.primitives import ( WrongPrimitivesVisitor, ) #: Used to store all token related visitors to be later passed to checker: TOKENS_PRESET = ( WrongCommentVisitor, WrongKeywordTokenVisitor, WrongPrimitivesVisitor, ) PK!uh6wemake_python_styleguide/visitors/tokenize/__init__.py# -*- coding: utf-8 -*- PK!Уi i 6wemake_python_styleguide/visitors/tokenize/comments.py# -*- coding: utf-8 -*- r""" Disallows to use incorrect magic comments. That's how a basic ``comment`` type token looks like: TokenInfo( type=57 (COMMENT), string='# noqa: Z100', start=(1, 4), end=(1, 16), line="u'' # noqa: Z100\n", ) """ import re import tokenize from typing import ClassVar from typing.re import Pattern from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.best_practices import ( WrongDocCommentViolation, WrongMagicCommentViolation, ) from wemake_python_styleguide.visitors.base import BaseTokenVisitor @final class WrongCommentVisitor(BaseTokenVisitor): """Checks comment tokens.""" noqa_check: ClassVar[Pattern] = re.compile(r'^noqa:?($|[A-Z\d\,\s]+)') type_check: ClassVar[Pattern] = re.compile( r'^type:\s?([\w\d\[\]\'\"\.]+)$', ) def _get_comment_text(self, token: tokenize.TokenInfo) -> str: return token.string[1:].strip() def _check_noqa(self, token: tokenize.TokenInfo) -> None: comment_text = self._get_comment_text(token) match = self.noqa_check.match(comment_text) if not match: return excludes = match.groups()[0].strip() if not excludes: # We can not pass the actual line here, # since it will be ignored due to `# noqa` comment: self.add_violation(WrongMagicCommentViolation(text=comment_text)) def _check_typed_ast(self, token: tokenize.TokenInfo) -> None: comment_text = self._get_comment_text(token) match = self.type_check.match(comment_text) if not match: return declared_type = match.groups()[0].strip() if declared_type != 'ignore': self.add_violation( WrongMagicCommentViolation(token, text=comment_text), ) def _check_empty_doc_comment(self, token: tokenize.TokenInfo) -> None: comment_text = self._get_comment_text(token) if comment_text == ':': self.add_violation(WrongDocCommentViolation(token)) def visit_comment(self, token: tokenize.TokenInfo) -> None: """ Performs comment checks. Raises: WrongDocCommentViolation WrongMagicCommentViolation """ self._check_noqa(token) self._check_typed_ast(token) self._check_empty_doc_comment(token) PK! qD6wemake_python_styleguide/visitors/tokenize/keywords.py# -*- coding: utf-8 -*- import keyword import tokenize from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.consistency import ( MissingSpaceBetweenKeywordAndParenViolation, ) from wemake_python_styleguide.visitors.base import BaseTokenVisitor @final class WrongKeywordTokenVisitor(BaseTokenVisitor): """Visits keywords and finds violations related to their usage.""" def _check_space_before_open_paren(self, token: tokenize.TokenInfo) -> None: if token.line[token.end[1]:].startswith('('): self.add_violation( MissingSpaceBetweenKeywordAndParenViolation(token), ) def visit_name(self, token: tokenize.TokenInfo) -> None: """ Check keywords related rules. Raises: MissingSpaceBetweenKeywordAndParenViolation """ if keyword.iskeyword(token.string): self._check_space_before_open_paren(token) PK!:q}558wemake_python_styleguide/visitors/tokenize/primitives.py# -*- coding: utf-8 -*- import tokenize from typing import ClassVar, FrozenSet from wemake_python_styleguide.types import final from wemake_python_styleguide.violations.consistency import ( BadNumberSuffixViolation, PartialFloatViolation, UnderscoredNumberViolation, UnicodeStringViolation, ) from wemake_python_styleguide.visitors.base import BaseTokenVisitor @final class WrongPrimitivesVisitor(BaseTokenVisitor): """Visits primitive types to find incorrect usages.""" _bad_number_suffixes: ClassVar[FrozenSet[str]] = frozenset(( 'X', 'O', 'B', 'E', )) def _check_underscored_number(self, token: tokenize.TokenInfo) -> None: if '_' in token.string: self.add_violation( UnderscoredNumberViolation(token, text=token.string), ) def _check_partial_float(self, token: tokenize.TokenInfo) -> None: if token.string.startswith('.') or token.string.endswith('.'): self.add_violation(PartialFloatViolation(token, text=token.string)) def _check_bad_number_suffixes(self, token: tokenize.TokenInfo) -> None: if any(char in token.string for char in self._bad_number_suffixes): self.add_violation( BadNumberSuffixViolation(token, text=token.string), ) def visit_string(self, token: tokenize.TokenInfo) -> None: """ Checks string declarations. ``u`` can only be the only prefix. You can not combine it with ``r``, ``b``, or ``f``. Raises: UnicodeStringViolation """ if token.string.startswith('u'): self.add_violation(UnicodeStringViolation(token, text=token.string)) def visit_number(self, token: tokenize.TokenInfo) -> None: """ Checks number declarations. Raises: UnderscoredNumberViolation PartialFloatViolation BadNumberSuffixViolation """ self._check_underscored_number(token) self._check_partial_float(token) self._check_bad_number_suffixes(token) PK!H";?9wemake_python_styleguide-0.3.0.dist-info/entry_points.txtNINK(I+ϋ劲-O TdT椦f%g&gY9Ch..PK!f000wemake_python_styleguide-0.3.0.dist-info/LICENSEMIT License Copyright (c) 2018 wemake.services Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK!H,TT.wemake_python_styleguide-0.3.0.dist-info/WHEEL 1 0 =Rn>ZD:_dhRUQTk-k]m`Q'iPK!H_/8 t1wemake_python_styleguide-0.3.0.dist-info/METADATAXkW۸_iӁ$}fMBt(P;úkq,W_00tZ:::}^M&?q;lyw؂)LdkSe<-E½K͠lzr6 UafRaa0Y sX&rswrM)tټ@01XBz"oZHgEV"o#ucn\1o2KfT^٪T e$3>_`"h3/P\VuhyPk1\wy& Дu:Y޸F:Bܒ/s㮑*8 O`j}m} -]-x6)Q Y(cky|mF)VۋS^йF~d g -doڎS`4E5u, zj]̎ЦBcf*h5l6o8dQ;xnu!ƛ=JNy#L8o[7AN2x$(E8gʁ($Y?P"X-ҭ}/eEz )W[ژ]HXIDd+ -q>WLHr?j9~q׎-'r^ (gb/4\?m`m6~r;^y8$W*_ad(x'_¬g=$<6ABja,fRF0ksgv(HS'4Z;M"wh,4 i3 7~8=Ofaij@ܦ~a3dֵnk^jZ/>Ndp[_||}J_z{IlbHwM~G":\ص{~>?_ӻd٢Z7?OzIoO]w1{G{`>\|\ ^ewn;'gj>Ma_YU=Ȥn( BJ번d)}\g22SXs^IeQYMGܜx(S5tnU<."nE+l,@WuH> {zod)iQwu}g;",mοaۊ[avd\Rb-+<ɚͷj6&ϛnoede[ϘZ"mKVveTg$>8z#Ne6\_$fQ'oK3~6hne[hdBxB8 9HB5}g83#+], Ԭb!;bnPi:Z`UVGGFT<9mnbbƎq+eFgGܐfL*E&b7V̽!}7שEݗ]XxnK6,BoOh +`vmcYF0c1!Lx_\<S?EcDu3؇ @4]Ѭ(` $ Bd L !J;M3^ ~k1鲟!f@x$E~7VВV1uC̴a>7E:1r_dn?8}ͅX+HFT64vm?4ue+ciR R'w(=ea}ܙDU 8rk>W &JHja(Ya@.L%=[y[/dKt " 1y2ؑeΆfRYAICc6JlYlء x(p+wwd 0Vd Gq:Wf6[, 8v-]馴MFA" &f=6ah6G#ŜN`o++״;ޒUe#5peu!@'L m,HL"$Zk]F.<|Ew$Ž6,pCP[\ B!љFV!KbhG_\mBI StjJb m!hڋ9 .#'1椧#fe6i.Hm`i)^őp ! e\ýY~DrzdӰXaަ Ti58g+3c+.΅.u'Y?Z%`70E[Zů2'醊 =ԙxJs0 6UE5 cOx=ʓ Te }ry 4̾=9Vp-u#fsl:H%J(.χϺEJJg7_E Q'JӰH;HhР{ K{qu@rH$r=JP]&w?2G+|NWz~aXpmCZF(AEmE-guΎۺWOOW*j(jQOI\i٪5DF>0dts\fn1mW‘2JPK!Ha|&1 ?/wemake_python_styleguide-0.3.0.dist-info/RECORDDz~w %&*e3 Oulw|{F1?vZ{mưpЩVNyI8I󥞁"8d)L~J˘<:˾UTtS}!~ ,l^ʤc"K;)6H0fۇ7m8 tcݲ^dZpQ:Gǹ@Qy'~#s!Jt1 {Ao is)E͇>LFݱ#V+"2~ {\ p7*y>;Z4 G;rkw< TRD2;𶸮tV8E"Lb~o5&`Nse<)&86l\w#d7?@ x wM\/Fdva^K8BĤy[- -OVp64 G9͢`cn|$9ը:REس ǻ2#@%etOI+SM4>:߅C 1ޝe+_\U?P_Dcai4R+,ӳPP4*1`ko0%t''@bQa>e`] n^qˇت:4bx~7[Ii. 6]w]h;oeEaۭ1}b ziQ(h[,O[LCuޒ9/Nn+(pr…<K7mҷRϏ>$*XiBHwaGr६ L[8*<~~@E5YkJ҆ąfq⠗6bwQet}X,=AD5e z˗Hp˙Έ,X XddqjZxܐ5iG%jQ^W. 7?}xC@vaJSB>#`9FĎ&eyCɓ8@\Dvb(f $sj6R;Tw8qբ7I~v$%Q@Ї^rf `:4c6,L(QZZ졆3L^ dSj.v6^:j K =pEr+I5`gr+jsgVүgW-C}pF~ vm!^/_+l ne_Nm'2̚ŏ[#yWuYwY}7wa]۬K ;*{n0[J0; aH.uNຸYӾFkx:n5/ !1e@;/F }vX\ܒ8S\IKtam F [c~պMîs| F*Ȟ1xg(BMýTM#KVK^Q *?l wP!w E$0A6KFjP(z^ NLaЋ?>F~u= A5 tA.^Cpzb>@eW6)gH7rqYpvj"K6*g U_=}A@_D2~]N]%#hjKb]L2Q0: 8O cS),'85~| dC>n'BsaKL\^ Z BF>"ϫ_1&BDSU8H8s$yG ^Oo2drn8 ҕLv{yߣ"](5,~DyoPK!uh$wemake_python_styleguide/__init__.pyPK!X&#Zwemake_python_styleguide/checker.pyPK!p+ + %wemake_python_styleguide/constants.pyPK!uh+""wemake_python_styleguide/logics/__init__.pyPK![ ,"wemake_python_styleguide/logics/filenames.pyPK!s,,wemake_python_styleguide/logics/functions.pyPK!7N@@*1wemake_python_styleguide/logics/imports.pyPK!ע(4wemake_python_styleguide/logics/nodes.pyPK!uh57wemake_python_styleguide/logics/variables/__init__.pyPK!(l""3m7wemake_python_styleguide/logics/variables/access.pyPK![x7:wemake_python_styleguide/logics/variables/name_nodes.pyPK!MW 3T>wemake_python_styleguide/logics/variables/naming.pyPK!uh,fLwemake_python_styleguide/options/__init__.pyPK!)@*Lwemake_python_styleguide/options/config.pyPK!fT ,jwemake_python_styleguide/options/defaults.pyPK!upp!qwemake_python_styleguide/types.pyPK!.m#ywemake_python_styleguide/version.pyPK!Y/{wemake_python_styleguide/violations/__init__.pyPK!&)**+|wemake_python_styleguide/violations/base.pyPK!SbUU5gwemake_python_styleguide/violations/best_practices.pyPK!pk8>8>1wemake_python_styleguide/violations/complexity.pyPK!u4A992/!wemake_python_styleguide/violations/consistency.pyPK! =4=4-[wemake_python_styleguide/violations/naming.pyPK!uh-wemake_python_styleguide/visitors/__init__.pyPK!uh1wemake_python_styleguide/visitors/ast/__init__.pyPK!ށ3kwemake_python_styleguide/visitors/ast/attributes.pyPK!k%V V 1Hwemake_python_styleguide/visitors/ast/builtins.pyPK!fO O 0wemake_python_styleguide/visitors/ast/classes.pyPK!hI4wemake_python_styleguide/visitors/ast/comparisons.pyPK!oqWW<wemake_python_styleguide/visitors/ast/complexity/__init__.pyPK!nё:Wwemake_python_styleguide/visitors/ast/complexity/counts.pyPK!ăpp<@wemake_python_styleguide/visitors/ast/complexity/function.pyPK!9 9 wemake_python_styleguide/visitors/ast/complexity/jones.pyPK!v#V :wemake_python_styleguide/visitors/ast/complexity/nested.pyPK!Sa : wemake_python_styleguide/visitors/ast/complexity/offset.pyPK!2wemake_python_styleguide/visitors/ast/functions.pyPK!f>Ɯ0+wemake_python_styleguide/visitors/ast/imports.pyPK!1ee1)wemake_python_styleguide/visitors/ast/keywords.pyPK!qO^aa0Bwemake_python_styleguide/visitors/ast/modules.pyPK!G1/xKwemake_python_styleguide/visitors/ast/naming.pyPK!wyy)eiwemake_python_styleguide/visitors/base.pyPK!|QR/%wemake_python_styleguide/visitors/decorators.pyPK!uh7wemake_python_styleguide/visitors/filenames/__init__.pyPK!f @bwemake_python_styleguide/visitors/filenames/wrong_module_name.pyPK!uh5wemake_python_styleguide/visitors/presets/__init__.pyPK!cys  7wemake_python_styleguide/visitors/presets/complexity.pyPK!4]wemake_python_styleguide/visitors/presets/general.pyPK!j3Rwemake_python_styleguide/visitors/presets/tokens.pyPK!uh6wemake_python_styleguide/visitors/tokenize/__init__.pyPK!Уi i 6wemake_python_styleguide/visitors/tokenize/comments.pyPK! qD6wemake_python_styleguide/visitors/tokenize/keywords.pyPK!:q}558ѩwemake_python_styleguide/visitors/tokenize/primitives.pyPK!H";?9\wemake_python_styleguide-0.3.0.dist-info/entry_points.txtPK!f000wemake_python_styleguide-0.3.0.dist-info/LICENSEPK!H,TT.lwemake_python_styleguide-0.3.0.dist-info/WHEELPK!H_/8 t1 wemake_python_styleguide-0.3.0.dist-info/METADATAPK!Ha|&1 ?/bwemake_python_styleguide-0.3.0.dist-info/RECORDPK99