PK!uh$wemake_python_styleguide/__init__.py# -*- coding: utf-8 -*- PK!"„ #wemake_python_styleguide/checker.py# -*- coding: utf-8 -*- from ast import Module from typing import Generator from wemake_python_styleguide.compat import maybe_set_parent from wemake_python_styleguide.options.config import Configuration from wemake_python_styleguide.types import CheckResult, ConfigurationOptions from wemake_python_styleguide.version import version from wemake_python_styleguide.visitors.complexity.function import ( FunctionComplexityVisitor, ) from wemake_python_styleguide.visitors.complexity.nested import ( NestedComplexityVisitor, ) from wemake_python_styleguide.visitors.wrong_class import WrongClassVisitor from wemake_python_styleguide.visitors.wrong_function_call import ( WrongFunctionCallVisitor, ) from wemake_python_styleguide.visitors.wrong_import import WrongImportVisitor from wemake_python_styleguide.visitors.wrong_keyword import ( WrongKeywordVisitor, WrongRaiseVisitor, ) from wemake_python_styleguide.visitors.wrong_name import ( WrongModuleMetadataVisitor, WrongNameVisitor, ) #: Visitors that should be working by default: ENABLED_VISITORS = ( # Styling and correctness: WrongRaiseVisitor, WrongFunctionCallVisitor, WrongImportVisitor, WrongKeywordVisitor, WrongNameVisitor, WrongModuleMetadataVisitor, WrongClassVisitor, # Complexity: FunctionComplexityVisitor, NestedComplexityVisitor, ) class Checker(object): """ Main checker class. Runs all checks that are bundled with this package. If you want to add new checks they should be added to ``ENABLED_VISITORS``. """ name = 'wemake-python-styleguide' version = version config = Configuration() options: ConfigurationOptions def __init__(self, tree: Module, filename: str = '-') -> None: """Creates new checker instance.""" self.tree = maybe_set_parent(tree) self.filename = filename @classmethod def add_options(cls, parser): # TODO: types """Calls Configuration instance method for registering options.""" cls.config.register_options(parser) @classmethod def parse_options(cls, options: ConfigurationOptions): """Parses registered options for providing to the visiter.""" cls.options = options def run(self) -> Generator[CheckResult, None, None]: """ Runs the checker. This method is used by `flake8` API. After all configuration is parsed and passed. """ for visitor_class in ENABLED_VISITORS: visiter = visitor_class(self.options) visiter.visit(self.tree) for error in visiter.errors: yield (*error.node_items(), type(self)) PK!pԄ"wemake_python_styleguide/compat.py# -*- coding: utf-8 -*- """ This module contains ugly hacks and fixes for version compat issues. Do not be over-exited to add anything here. """ import ast def maybe_set_parent(tree: ast.AST) -> ast.AST: """Sets parents for all nodes that do not have this prop.""" for statement in ast.walk(tree): for child in ast.iter_child_nodes(statement): if not hasattr(child, 'parent'): # noqa: Z113 setattr(child, 'parent', statement) # noqa: Z113 return tree PK!Ti%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 sys from typing import Tuple #: List of functions we forbid to use. BAD_FUNCTIONS = frozenset(( # Code generation: 'eval', 'exec', 'compile', # Magic: 'globals', 'locals', 'vars', 'dir', # IO: 'input', # Attribute access: 'hasattr', 'delattr', # Misc: 'copyright', 'help', # Dynamic imports: '__import__', # OOP: 'staticmethod', )) #: List of module metadata we forbid to use. BAD_MODULE_METADATA_VARIABLES = frozenset(( '__author__', '__all__', '__version__', '__about__', )) _BAD_VARIABLE_NAMES: Tuple[str, ...] = ( 'data', 'result', 'results', 'item', 'items', 'value', 'values', 'val', 'vals', 'var', 'vars', 'content', 'contents', 'info', 'handle', 'handler', 'file', 'klass', ) if sys.version_info < (3, 7): # pragma: no cover _BAD_VARIABLE_NAMES += ( # Compatibility with `python3.7`: 'async', 'await', ) #: List of variable names we forbid to use. BAD_VARIABLE_NAMES = frozenset(_BAD_VARIABLE_NAMES) #: List of magic methods that are forbiden to use. BAD_MAGIC_METHODS = frozenset(( '__del__', # since we don't use `del` '__delitem__', # since we don't use `del` '__delete__', # since we don't use `del` '__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 = frozenset(( 'Meta', )) #: List of nested functions' names we allow to use. NESTED_FUNCTIONS_WHITELIST = frozenset(( 'decorator', 'factory', )) #: List of allowed ``__future__`` imports. FUTURE_IMPORTS_WHITELIST = frozenset(( 'annotations', 'generator_stop', )) PK!+wemake_python_styleguide/errors/__init__.pyPK!@'wemake_python_styleguide/errors/base.py# -*- coding: utf-8 -*- from ast import AST from typing import Tuple class BaseStyleViolation(object): """ This is a base class for all style errors. It basically just defines how to create any error and how to format this error later on. Each subclass must define ``_error_tmpl`` and ``_code`` fields. """ _error_tmpl: str _code: str def __init__(self, node: AST, text: str = None) -> None: """Creates new instance of style error.""" self.node = node if text is None: self._text = node.__class__.__name__.lower() else: self._text = text def message(self) -> str: """ Returns error's formated message. >>> import ast >>> from wemake_python_styleguide.errors.general import ( ... WrongKeywordViolation, ... ) >>> error = WrongKeywordViolation(ast.Pass()) >>> error.message() 'Z110 Found wrong keyword "pass"' >>> error = WrongKeywordViolation(ast.Delete(), text='del') >>> error.message() 'Z110 Found wrong keyword "del"' """ return self._error_tmpl.format(self._code, self._text) def node_items(self) -> Tuple[int, int, str]: """Returns `Tuple` to match `flake8` API format.""" lineno = getattr(self.node, 'lineno', 0) col_offset = getattr(self.node, 'col_offset', 0) return lineno, col_offset, self.message() PK!eHH*wemake_python_styleguide/errors/classes.py# -*- coding: utf-8 -*- """These rules checks ``class``es to be defined correctly.""" from wemake_python_styleguide.errors.base import BaseStyleViolation class StaticMethodViolation(BaseStyleViolation): """ This rule forbids to use ``@staticmethod`` decorator. Use regular methods, ``classmethods``, or raw functions instead. Note: Returns Z300 as error code """ _error_tmpl = '{0} Found using staticmethod "{1}"' _code = 'Z300' class BadMagicMethodViolation(BaseStyleViolation): """ This rule forbids to use some magic methods. Note: Returns Z301 as error code """ _error_tmpl = '{0} Found using restricted magic method "{1}"' _code = 'Z301' class RequiredBaseClassViolation(BaseStyleViolation): """ This rule forbids to write classes without base classes. Example:: # Correct: class Some(object): ... # Wrong: class Some: ... Note: Returns Z302 as error code """ _error_tmpl = '{0} Found class without a base class "{1}"' _code = 'Z302' PK!o9-wemake_python_styleguide/errors/complexity.py# -*- coding: utf-8 -*- """ These checks finds flaws in your application design. What we call "design flaws": 1. Complex code (there are a lof of complexity checks!) 2. Nested classes, functions """ from wemake_python_styleguide.errors.base import BaseStyleViolation class NestedFunctionViolation(BaseStyleViolation): """ This rule forbids to have nested functions. Just write flat functions, there's no need to nest them. However, there are some whitelisted names like, see ``NESTED_FUNCTIONS_WHITELIST`` for the whole list. We also disallow to nest ``lambda``s. Example:: # Correct: def do_some(): ... def other(): ... # Wrong: def do_some(): def inner(): ... Note: Returns Z200 as error code """ _error_tmpl = '{0} Found nested function "{1}"' _code = 'Z200' class NestedClassViolation(BaseStyleViolation): """ This rule forbids to have nested classes. Just write flat classes, there's no need nest them. However, there are some whitelisted class names like: ``Meta``. Example:: # Wrong: class Some: class Inner: ... Note: Returns Z201 as error code """ _error_tmpl = '{0} Found nested class "{1}"' _code = 'Z201' class TooManyLocalsViolation(BaseStyleViolation): """ This rule forbids to have too many local variables in the unit of code. 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. 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. Note: Returns Z202 as error code """ _error_tmpl = '{0} Found too many local variables "{1}"' _code = 'Z202' class TooManyArgumentsViolation(BaseStyleViolation): """ This rule forbids to have too many arguments for a function or method. This is an indecator of a bad desing. When function requires many arguments it shows that it is required to refactor this piece of code. Note: Returns Z203 as error code """ _error_tmpl = '{0} Found too many arguments "{1}"' _code = 'Z203' class TooManyBranchesViolation(BaseStyleViolation): """ This rule forbids to have to many branches in a function. When there are too many branches, functions are hard to test. They are also hard to read and hard to change and read. Note: Returns Z204 as error code """ _error_tmpl = '{0} Found too many branches "{1}"' _code = 'Z204' class TooManyReturnsViolation(BaseStyleViolation): """ This rule forbids placing too many ``return`` statements into the function. When there are too many ``return`` keywords, functions are hard to test. They are also hard to read and hard to change and read. Note: Returns Z205 as error code """ _error_tmpl = '{0} Found too many return statements "{1}"' _code = 'Z205' class TooManyExpressionsViolation(BaseStyleViolation): """ This rule forbids putting to many expression is a unit of code. Because when there are too many expression, it means, that code has some logical or structural problems. We only have to identify them. Note: Returns Z206 as error code """ _error_tmpl = '{0} Found too many expressions "{1}"' _code = 'Z206' class TooDeepNestingViolation(BaseStyleViolation): """ This rule forbids nesting blocks too deep. If nesting is too deep that indicates of another problem, that there's to many things going on at the same time. So, we need to check these cases before they have made their way to production. Note: Returns Z207 as error code """ _error_tmpl = '{0} Found too deep nesting "{1}"' _code = 'Z207' PK!ݿ}==*wemake_python_styleguide/errors/general.py# -*- coding: utf-8 -*- """ These rules checks some general rules. Like: 1. Naming 2. Using some builtins 3. Using keywords 4. Working with exceptions """ from wemake_python_styleguide.errors.base import BaseStyleViolation class WrongKeywordViolation(BaseStyleViolation): """ This rule forbids to use some keywords from ``python``. We do this, since some keywords are anti-patterns. Example:: # Wrong: pass exec eval Note: Returns Z110 as error code """ _error_tmpl = '{0} Found wrong keyword "{1}"' _code = 'Z110' class BareRiseViolation(BaseStyleViolation): """ This rule forbids using bare `raise` keyword outside of `except` block. This may be a serious error in your application, so we should prevent that. Example:: # Correct: raise ValueError('Value is too low') # Wrong: raise Note: Returns Z111 as error code """ _error_tmpl = '{0} Found bare raise outside of except "{1}"' _code = 'Z111' class RaiseNotImplementedViolation(BaseStyleViolation): """ This rule forbids to use `NotImplemented` error. These two errors have different use cases. Use cases of `NotImplemented` is too limited to be generally available. Example:: # Correct: raise NotImplementedError('To be done') # Wrong: raise NotImplemented See Also: https://stackoverflow.com/a/44575926/4842742 Note: Returns Z112 as error code """ _error_tmpl = '{0} Found raise NotImplemented "{1}"' _code = 'Z112' class WrongFunctionCallViolation(BaseStyleViolation): """ This rule forbids to call some built-in functions. Since some functions are only suitable for very specific usecases, we forbid to use them in a free manner. Note: Returns Z113 as error code """ _error_tmpl = '{0} Found wrong function call "{1}"' _code = 'Z113' class WrongVariableNameViolation(BaseStyleViolation): """ This rule forbids to have blacklisted variable names. Example:: # Correct: html_node = None # Wrong: item = None Note: Returns Z114 as error code """ _error_tmpl = '{0} Found wrong variable name "{1}"' _code = 'Z114' class TooShortVariableNameViolation(BaseStyleViolation): """ This rule forbids to have too short variable names. Example:: # Correct: x_coord = 1 # Wrong: x = 1 Note: Returns Z115 as error code """ _error_tmpl = '{0} Found too short name "{1}"' _code = 'Z115' class PrivateNameViolation(BaseStyleViolation): """ This rule forbids to have private name pattern. It includes: variables, attributes, functions, and methods. Example:: # Correct: def _collect_coverage(self): ... # Wrong: def __collect_coverage(self): ... Note: Returns Z116 as error code """ _error_tmpl = '{0} Found private name pattern "{1}"' _code = 'Z116' class WrongModuleMetadataViolation(BaseStyleViolation): """ This rule forbids to have some module level variables. We discourage using module variables like ``__author__``, because there's no need in them. Use proper docstrings and classifiers. Packaging should not be done in code. Example:: # Wrong: __author__ = 'Nikita Sobolev' Note: Returns Z117 as error code """ _error_tmpl = '{0} Found wrong metadata variable {1}' _code = 'Z117' PK!VkQ Q *wemake_python_styleguide/errors/imports.py# -*- coding: utf-8 -*- """These rules checks ``import``s to be defined correctly.""" from wemake_python_styleguide.errors.base import BaseStyleViolation class LocalFolderImportViolation(BaseStyleViolation): """ This rule forbids to have imports relative to the current folder. Example:: # Correct: from my_package.version import get_version # Wrong: from .version import get_version from ..drivers import MySQLDriver Note: Returns Z100 as error code """ _error_tmpl = '{0} Found local folder import "{1}"' _code = 'Z100' class NestedImportViolation(BaseStyleViolation): """ This rule forbids to have nested imports in functions. Nested imports show that there's an issue with you design. So, you don't need nested imports, you need to refactor your code. Example:: # Wrong: def some(): from my_module import some_function Note: Returns Z101 as error code """ _error_tmpl = '{0} Found nested import "{1}"' _code = 'Z101' class FutureImportViolation(BaseStyleViolation): """ This rule forbids to use ``__future__`` imports. Almost all ``__future__`` imports are legacy ``python2`` compatibility tools that are no longer required. Except, there are some new ones for ``python4`` support. Example:: # Correct: from __future__ import annotations # Wrong: from __future__ import print_function Note: Returns Z102 as error code """ _error_tmpl = '{0} Found future import "{1}"' _code = 'Z102' class DottedRawImportViolation(BaseStyleViolation): """ This rule forbids to use imports like ``import os.path``. Example:: # Correct: from os import path # Wrong: import os.path Note: Returns Z103 as error code """ _error_tmpl = '{0} Found dotted raw import "{1}"' _code = 'Z103' class SameAliasImportViolation(BaseStyleViolation): """ This rule forbids to use the same alias as the original name in imports. Example:: # Correct: from os import path # Wrong: from os import path as path Note: Returns Z104 as error code """ _error_tmpl = '{0} Found same alias import "{1}"' _code = 'Z104' PK!uh+wemake_python_styleguide/logics/__init__.py# -*- coding: utf-8 -*- PK!`Ө,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 either or not given function type belongs to a class. >>> is_method('function') False >>> is_method(None) False >>> is_method('method') True >>> is_method('classmethod') True """ return function_type in ['method', 'classmethod'] PK!=)wemake_python_styleguide/logics/limits.py# -*- coding: utf-8 -*- def has_just_exceeded_limit(current_value: int, limit: int) -> bool: """ Check either value has just exceeded its limit or not. >>> has_just_exceeded_limit(1, 2) False >>> has_just_exceeded_limit(1, 1) False >>> has_just_exceeded_limit(2, 1) True >>> has_just_exceeded_limit(3, 1) False """ return current_value == limit + 1 PK!BSS,wemake_python_styleguide/logics/variables.py# -*- coding: utf-8 -*- from typing import Iterable, Optional 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_too_short_variable_name( name: Optional[str], min_length: int = 2, ) -> bool: """ Checks for too short variable names. >>> is_too_short_variable_name('test') False >>> is_too_short_variable_name(None) 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 is not None and name != '_' and len(name) < min_length def is_private_variable(name: Optional[str]) -> bool: """ Checks if variable has private name pattern. >>> is_private_variable(None) False >>> is_private_variable('regular') False >>> is_private_variable('__private') True >>> is_private_variable('_protected') False >>> is_private_variable('__magic__') False """ return ( name is not None and name.startswith('__') and not name.endswith('__') ) PK!uh,wemake_python_styleguide/options/__init__.py# -*- coding: utf-8 -*- PK!R *wemake_python_styleguide/options/config.py# -*- coding: utf-8 -*- from wemake_python_styleguide.options import defaults class Configuration(object): """ Provides configuration options for `wemake-python-styleguide` plugin. You can adjust configuration via CLI option: Example:: flake8 --max-returns 7 You can also provide configuration options in `tox.ini` or `setup.cfg`: Example:: [flake8] max-returns = 7 We support the following options: - `max-returns` - maximum allowed number of ``return`` statements in one function, defaults to ``MAX_RETURNS`` - `max-local-variables` - maximum allowed number of local variables in one function, defaults to ``MAX_LOCAL_VARIABLES`` - `max-expressions` - maximum allowed number of expressions in one function, defaults to ``MAX_EXPRESSIONS`` - `max-arguments` - maximum allowed number of arguments in one function, defaults to ``MAX_ARGUMENTS`` - `min-variable-length` - minimum number of chars to define a valid variable name, defaults to ``MIN_VARIABLE_LENGTH`` """ def register_options(self, parser) -> None: # TODO: types """Registers options for our plugin.""" parser.add_option( '--max-returns', parse_from_config=True, type='int', default=defaults.MAX_RETURNS, help='Maximum allowed number of return statements in one function.', ) parser.add_option( '--max-local-variables', parse_from_config=True, type='int', default=defaults.MAX_LOCAL_VARIABLES, help='Maximum allowed number of local variables in one function.', ) parser.add_option( '--max-expressions', parse_from_config=True, type='int', default=defaults.MAX_EXPRESSIONS, help='Maximum allowed number of expressions in one function.', ) parser.add_option( '--max-arguments', parse_from_config=True, type='int', default=defaults.MAX_ARGUMENTS, help='Maximum allowed number of arguments in one function.', ) parser.add_option( '--min-variable-length', parse_from_config=True, type='int', default=defaults.MIN_VARIABLE_LENGTH, help='Minimum required length of the variable name.', ) PK!?Pbb,wemake_python_styleguide/options/defaults.py# -*- coding: utf-8 -*- """ Constants with default values for 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, if you find them too strict or too permissive. """ #: Maximum number of `return` statements allowed in a single function: MAX_RETURNS = 5 #: Maximum number of local variables in a function: MAX_LOCAL_VARIABLES = 5 #: Maximum number of expressions in a single function: MAX_EXPRESSIONS = 9 #: Maximum number of arguments for functions or method, `self` is not counted: MAX_ARGUMENTS = 5 #: Minimum variable's name length: MIN_VARIABLE_LENGTH = 2 PK! wemake_python_styleguide/temp.pyPK!<-OO!wemake_python_styleguide/types.py# -*- coding: utf-8 -*- """ This module contains custom `mypy` types that we commonly use. General rule is: if there's a complex type, put it here. """ import ast from typing import Tuple, Union from typing_extensions import Protocol #: In cases we need to work with both import types: AnyImport = Union[ast.Import, ast.ImportFrom] #: Flake8 API format to return error messages: CheckResult = Tuple[int, int, str, type] class ConfigurationOptions(Protocol): """ This class provides structure for the options we use in our checker. It uses structural subtyping, and does not represent any kind of a real class or structure. See: https://mypy.readthedocs.io/en/latest/protocols.html """ max_arguments: int max_local_variables: int max_returns: int max_expressions: int min_variable_length: int PK!e[#wemake_python_styleguide/version.py# -*- coding: utf-8 -*- import pkg_resources #: We store the version number inside the `pyproject.toml`: version = pkg_resources.get_distribution('wemake-python-styleguide').version PK!uh-wemake_python_styleguide/visitors/__init__.py# -*- coding: utf-8 -*- PK!uh2wemake_python_styleguide/visitors/base/__init__.py# -*- coding: utf-8 -*- PK!{ff1wemake_python_styleguide/visitors/base/visitor.py# -*- coding: utf-8 -*- from ast import NodeVisitor from typing import List from wemake_python_styleguide.errors.base import BaseStyleViolation from wemake_python_styleguide.types import ConfigurationOptions class BaseNodeVisitor(NodeVisitor): """ This class allows to store errors while traversing node tree. Attributes: options: contains the options objects passed and parsed by `flake8`. errors: list of errors for the specific checker. """ options: ConfigurationOptions def __init__(self, options: ConfigurationOptions) -> None: """Creates new visitor instance.""" super().__init__() self.options = options self.errors: List[BaseStyleViolation] = [] def add_error(self, error: BaseStyleViolation) -> None: """Adds error to the visitor.""" self.errors.append(error) PK!Nuՠ]]8wemake_python_styleguide/visitors/complexity/__init__.py# -*- coding: utf-8 -*- # TODO: implement TooDeepNestingViolation, TooManyBranchesViolation PK!n ~118wemake_python_styleguide/visitors/complexity/function.py# -*- coding: utf-8 -*- import ast from collections import defaultdict from typing import DefaultDict, List from wemake_python_styleguide.errors.complexity import ( TooManyArgumentsViolation, TooManyExpressionsViolation, TooManyLocalsViolation, TooManyReturnsViolation, ) from wemake_python_styleguide.logics.functions import is_method from wemake_python_styleguide.logics.limits import has_just_exceeded_limit from wemake_python_styleguide.types import ConfigurationOptions from wemake_python_styleguide.visitors.base.visitor import BaseNodeVisitor class FunctionComplexityVisitor(BaseNodeVisitor): """ This class checks for complexity inside functions. This includes: 1. Number of arguments 2. Number of `return`s 3. Number of expressions 4. Number of local variables """ def __init__(self, options: ConfigurationOptions) -> None: """Creates config parser instance and counters for tracked metrics.""" super().__init__(options) self.expressions: DefaultDict[str, int] = defaultdict(int) self.variables: DefaultDict[str, List[str]] = defaultdict(list) self.returns: DefaultDict[str, int] = defaultdict(int) def _check_arguments_count(self, node: ast.FunctionDef): counter = 0 has_extra_self_or_cls = 0 max_arguments_count = self.options.max_arguments if is_method(getattr(node, 'function_type', None)): has_extra_self_or_cls = 1 counter += len(node.args.args) counter += len(node.args.kwonlyargs) if node.args.vararg: counter += 1 if node.args.kwarg: counter += 1 if counter > max_arguments_count + has_extra_self_or_cls: self.add_error( TooManyArgumentsViolation(node, text=node.name), ) # TODO: move this logics inside into another place: def _update_variables(self, function: ast.FunctionDef, variable_name: str): """ Increases the counter of local variables. What is treated as local variable? Check ``TooManyLocalsViolation`` documentation. """ max_local_variables_count = self.options.max_local_variables function_variables = self.variables[function.name] if variable_name not in function_variables and variable_name != '_': function_variables.append(variable_name) limit_exceeded = has_just_exceeded_limit( len(function_variables), max_local_variables_count, ) if limit_exceeded: self.add_error( TooManyLocalsViolation(function, text=function.name), ) # TODO: move this logics inside into another place: def _update_returns(self, function: ast.FunctionDef): max_returns_count = self.options.max_returns self.returns[function.name] += 1 limit_exceeded = has_just_exceeded_limit( self.returns[function.name], max_returns_count, ) if limit_exceeded: self.add_error( TooManyReturnsViolation(function, text=function.name), ) # TODO: move this logics inside into another place: def _update_expression(self, function: ast.FunctionDef): max_expressions_count = self.options.max_expressions self.expressions[function.name] += 1 limit_exceeded = has_just_exceeded_limit( self.expressions[function.name], max_expressions_count, ) if limit_exceeded: self.add_error( TooManyExpressionsViolation(function, text=function.name), ) def _check_function_complexity(self, node: ast.FunctionDef): """ 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): 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, getattr(sub_node, 'id')) if isinstance(sub_node, ast.Return): self._update_returns(node) if isinstance(sub_node, ast.Expr): self._update_expression(node) def visit_FunctionDef(self, node: ast.FunctionDef): """ Checks function's internal complexity. Raises: - TooManyExpressionsViolation - TooManyReturnsViolation - TooManyLocalsViolation - TooManyArgumentsViolation """ self._check_arguments_count(node) self._check_function_complexity(node) self.generic_visit(node) PK!9-4 4 6wemake_python_styleguide/visitors/complexity/nested.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.constants import ( NESTED_CLASSES_WHITELIST, NESTED_FUNCTIONS_WHITELIST, ) from wemake_python_styleguide.errors.complexity import ( NestedClassViolation, NestedFunctionViolation, ) from wemake_python_styleguide.visitors.base.visitor import BaseNodeVisitor class NestedComplexityVisitor(BaseNodeVisitor): """ This class 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. """ def visit_ClassDef(self, node: ast.ClassDef): """ Used to find nested classes in other classes and functions. Uses ``NESTED_CLASSES_WHITELIST`` to respect some nested classes. Raises: - NestedClassViolation """ parent = getattr(node, 'parent', None) is_inside_class = isinstance(parent, ast.ClassDef) is_inside_function = isinstance(parent, ast.FunctionDef) if is_inside_class and node.name not in NESTED_CLASSES_WHITELIST: self.add_error(NestedClassViolation(node, text=node.name)) elif is_inside_function: self.add_error(NestedClassViolation(node, text=node.name)) self.generic_visit(node) def visit_FunctionDef(self, node: ast.FunctionDef): """ Used to find nested functions. Uses ``NESTED_FUNCTIONS_WHITELIST`` to respect some nested functions. Respected usecases for nested functions: 1. decorator 2. factory function Raises: - NestedFunctionViolation """ parent = getattr(node, 'parent', None) is_inside_function = isinstance(parent, ast.FunctionDef) if is_inside_function and node.name not in NESTED_FUNCTIONS_WHITELIST: self.add_error(NestedFunctionViolation(node, text=node.name)) self.generic_visit(node) def visit_Lambda(self, node: ast.Lambda): """ Used to find nested ``lambda``s. Raises: - NestedFunctionViolation """ parent = getattr(node, 'parent', None) if isinstance(parent, ast.Lambda): self.add_error(NestedFunctionViolation(node)) self.generic_visit(node) PK!,__0wemake_python_styleguide/visitors/wrong_class.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.constants import BAD_MAGIC_METHODS from wemake_python_styleguide.errors.classes import ( BadMagicMethodViolation, RequiredBaseClassViolation, StaticMethodViolation, ) from wemake_python_styleguide.visitors.base.visitor import BaseNodeVisitor class WrongClassVisitor(BaseNodeVisitor): """ This class is responsible for restricting some ``class`` antipatterns. Here we check for stylistic issues and design patterns. """ def _check_decorators(self, node: ast.FunctionDef): decorators = getattr(node, 'decorator_list', []) for decorator in decorators: name = getattr(decorator, 'id', None) if name == 'staticmethod': self.add_error(StaticMethodViolation(node, text=node.name)) def _check_magic_methods(self, node: ast.FunctionDef): if node.name in BAD_MAGIC_METHODS: self.add_error(BadMagicMethodViolation(node, text=node.name)) def _check_base_class(self, node: ast.ClassDef): if len(node.bases) == 0: self.add_error(RequiredBaseClassViolation(node, text=node.name)) def visit_ClassDef(self, node: ast.ClassDef): """ Checking class definitions. Used to find: 1. Base class violations Raises: - RequiredBaseClassViolation """ self._check_base_class(node) self.generic_visit(node) def visit_FunctionDef(self, node: ast.FunctionDef): """ Checking class methods. Used to find: 1. `@staticmethod` decorators 2. Detect forbiden magic methods Raises: - StaticMethodViolation - BadMagicMethodViolation """ self._check_decorators(node) self._check_magic_methods(node) self.generic_visit(node) PK!c4+8wemake_python_styleguide/visitors/wrong_function_call.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.constants import BAD_FUNCTIONS from wemake_python_styleguide.errors.general import WrongFunctionCallViolation from wemake_python_styleguide.logics.functions import given_function_called from wemake_python_styleguide.visitors.base.visitor import BaseNodeVisitor class WrongFunctionCallVisitor(BaseNodeVisitor): """ This class is responsible for restricting some dangerous function calls. All these functions are defined in `BAD_FUNCTIONS`. """ def visit_Call(self, node: ast.Call): """ Used to find `BAD_FUNCTIONS` calls. Raises: - WrongFunctionCallViolation """ function_name = given_function_called(node, BAD_FUNCTIONS) if function_name: self.add_error(WrongFunctionCallViolation( node, text=function_name, )) self.generic_visit(node) PK!E4@ @ 1wemake_python_styleguide/visitors/wrong_import.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.constants import FUTURE_IMPORTS_WHITELIST from wemake_python_styleguide.errors.imports import ( DottedRawImportViolation, FutureImportViolation, LocalFolderImportViolation, NestedImportViolation, SameAliasImportViolation, ) from wemake_python_styleguide.types import AnyImport from wemake_python_styleguide.visitors.base.visitor import BaseNodeVisitor class WrongImportVisitor(BaseNodeVisitor): """This class is responsible for finding wrong imports.""" def _get_error_text(self, node: ast.AST) -> str: module = getattr(node, 'module', None) if module is not None: return module if isinstance(node, ast.Import): return node.names[0].name return '.' def _check_nested_import(self, node: ast.AST): text = self._get_error_text(node) parent = getattr(node, 'parent', None) if not isinstance(parent, ast.Module): self.add_error(NestedImportViolation(node, text=text)) def _check_local_import(self, node: ast.ImportFrom): text = self._get_error_text(node) if node.level != 0: self.add_error(LocalFolderImportViolation(node, text=text)) def _check_future_import(self, node: ast.ImportFrom): if node.module == '__future__': for alias in node.names: if alias.name not in FUTURE_IMPORTS_WHITELIST: self.add_error( FutureImportViolation(node, text=alias.name), ) def _check_dotted_raw_import(self, node: ast.Import): for alias in node.names: if '.' in alias.name: self.add_error(DottedRawImportViolation(node, text=alias.name)) def _check_alias(self, node: AnyImport): for alias in node.names: if alias.asname == alias.name: self.add_error(SameAliasImportViolation(node, text=alias.name)) def visit_Import(self, node: ast.Import): """ Used to find wrong `import` statements. Raises: - SameAliasImportViolation - DottedRawImportViolation - NestedImportViolation """ self._check_nested_import(node) self._check_dotted_raw_import(node) self._check_alias(node) self.generic_visit(node) def visit_ImportFrom(self, node: ast.ImportFrom): """ Used to find wrong `from import` statements. Raises: - SameAliasImportViolation - NestedImportViolation - LocalFolderImportViolation - FutureImportViolation """ self._check_local_import(node) self._check_nested_import(node) self._check_future_import(node) self._check_alias(node) self.generic_visit(node) PK!. 2wemake_python_styleguide/visitors/wrong_keyword.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.errors.general import ( BareRiseViolation, RaiseNotImplementedViolation, WrongKeywordViolation, ) from wemake_python_styleguide.visitors.base.visitor import BaseNodeVisitor class WrongRaiseVisitor(BaseNodeVisitor): """This class finds wrong `raise` keywords.""" def _check_exception_type(self, node: ast.Raise, exception) -> None: exception_func = getattr(exception, 'func', None) if exception_func: exception = exception_func exception_name = getattr(exception, 'id', None) if exception_name == 'NotImplemented': self.add_error( RaiseNotImplementedViolation(node, text=exception_name), ) def _check_bare_raise(self, node: ast.Raise) -> None: parent = getattr(node, 'parent', None) if not isinstance(parent, ast.ExceptHandler): self.add_error(BareRiseViolation(node)) def visit_Raise(self, node: ast.Raise) -> None: """ Checks how `raise` keyword is used. Raises: - RaiseNotImplementedViolation - BareRiseViolation """ exception = getattr(node, 'exc', None) if not exception: self._check_bare_raise(node) else: self._check_exception_type(node, exception) self.generic_visit(node) class WrongKeywordVisitor(BaseNodeVisitor): """This class is responsible for finding wrong keywords.""" def visit_Global(self, node: ast.Global): """ Used to find `global` keyword. Raises: - WrongKeywordViolation """ self.add_error(WrongKeywordViolation(node)) self.generic_visit(node) def visit_Nonlocal(self, node: ast.Nonlocal): """ Used to find `nonlocal` keyword. Raises: - WrongKeywordViolation """ self.add_error(WrongKeywordViolation(node)) self.generic_visit(node) def visit_Delete(self, node: ast.Delete): """ Used to find `del` keyword. Raises: - WrongKeywordViolation """ self.add_error(WrongKeywordViolation(node, text='del')) self.generic_visit(node) def visit_Pass(self, node: ast.Pass): """ Used to find `pass` keyword. Raises: - WrongKeywordViolation """ self.add_error(WrongKeywordViolation(node)) self.generic_visit(node) PK!Dә6WW/wemake_python_styleguide/visitors/wrong_name.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.constants import ( BAD_MODULE_METADATA_VARIABLES, BAD_VARIABLE_NAMES, ) from wemake_python_styleguide.errors.general import ( PrivateNameViolation, TooShortVariableNameViolation, WrongModuleMetadataViolation, WrongVariableNameViolation, ) from wemake_python_styleguide.logics.variables import ( is_private_variable, is_too_short_variable_name, is_wrong_variable_name, ) from wemake_python_styleguide.types import AnyImport from wemake_python_styleguide.visitors.base.visitor import BaseNodeVisitor class WrongNameVisitor(BaseNodeVisitor): """ This class performs checks based on variable names. It is responsible for finding short and blacklisted variables, functions, and arguments. """ def _check_name(self, node: ast.AST, arg: str) -> None: if is_wrong_variable_name(arg, BAD_VARIABLE_NAMES): self.add_error(WrongVariableNameViolation(node, text=arg)) min_length = self.options.min_variable_length if is_too_short_variable_name(arg, min_length=min_length): self.add_error(TooShortVariableNameViolation(node, text=arg)) if is_private_variable(arg): self.add_error(PrivateNameViolation(node, text=arg)) def _check_function_signature(self, node: ast.FunctionDef) -> 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 visit_Attribute(self, node: ast.Attribute): """ Used to find wrong attribute names inside classes. Raises: - WrongVariableNameViolation - TooShortVariableNameViolation - PrivateNameViolation """ context = getattr(node, 'ctx', None) if isinstance(context, ast.Store): self._check_name(node, node.attr) self.generic_visit(node) def visit_FunctionDef(self, node: ast.FunctionDef): """ Used to find wrong function and method parameters. Raises: - WrongVariableNameViolation - TooShortVariableNameViolation - PrivateNameViolation """ name = getattr(node, 'name', None) self._check_name(node, name) self._check_function_signature(node) self.generic_visit(node) def visit_ExceptHandler(self, node: ast.ExceptHandler): """ Used to find wrong exception instances in ``try``/``except``. Raises: - WrongVariableNameViolation - TooShortVariableNameViolation - PrivateNameViolation """ name = getattr(node, 'name', None) self._check_name(node, name) self.generic_visit(node) def visit_Name(self, node: ast.Name): """ Used to find wrong regular variables. Raises: - WrongVariableNameViolation - TooShortVariableNameViolation - PrivateNameViolation """ context = getattr(node, 'ctx', None) if isinstance(context, ast.Store): self._check_name(node, node.id) self.generic_visit(node) def visit_Import(self, node: AnyImport): """ Used to check wrong import alias names. Raises: - WrongVariableNameViolation - TooShortVariableNameViolation - PrivateNameViolation """ for alias in node.names: if alias.asname: self._check_name(node, alias.asname) self.generic_visit(node) visit_ImportFrom = visit_Import class WrongModuleMetadataVisitor(BaseNodeVisitor): """This class finds wrong metadata information of a module.""" def _check_metadata(self, node: ast.Assign): 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') if target_node_id in BAD_MODULE_METADATA_VARIABLES: self.add_error( WrongModuleMetadataViolation(node, text=target_node_id), ) def visit_Assign(self, node: ast.Assign): """ Used to find the bad metadata variable names. Raises: - WrongModuleMetadataViolation """ self._check_metadata(node) self.generic_visit(node) PK!H";?9wemake_python_styleguide-0.0.7.dist-info/entry_points.txtNINK(I+ϋ劲-O TdT椦f%g&gY9Ch..PK!f000wemake_python_styleguide-0.0.7.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_zTT.wemake_python_styleguide-0.0.7.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]n0H*J>mlcAPK!HZgC1 1wemake_python_styleguide-0.0.7.dist-info/METADATAWms8_2 &G$e˙א fn^l[r%B~lhmzM{ X}jwM}ibWId1]@)YP`}=k1*$:qC X[\2u.b4Bjj0퉸!.?I\!ewbJH_UL* 3bUg>>}h5"3@&,!I \:UZ%GD( l/`X]-_PK!H /wemake_python_styleguide-0.0.7.dist-info/RECORDK[78F@Aʄ 6"kwթ뉪㩎{{_\rA 8WI6qM}K7#C4W1fIxE;ӹܖ0C˾4ةNBPL$*0A}2Ow<0iE9T@fE]J@ rv+|O553$Bc1i"1F5CU{׼p盟Y(OB`I߷SI1@}K]V6h$fY{S\oKQS@ Sqox+ݽ߭FamL2v{Ji$uukXQ C#*Y9,t=B; oT"8b>G<${'ʼ+}!h|ʕQ3ۖ!>W"K':/qةl]N\dxEAnnP8w!U,q(yj %VaoSL*^ z =yֳ4]s>:mvѶjH`ճjm Ny! A$;og;W9j݋FY6aCmuJ@8A סT0ɍ{;Ɂ L T5 {[O @p+y:MT8f:9C/M2$~5Hl$=ZJcY]ivG/7mh [,i[-;EZ*;Q= M-sǰsC? (vr\BuSMĴkO