PK!uh$wemake_python_styleguide/__init__.py# -*- coding: utf-8 -*- PK!5#wemake_python_styleguide/checker.py# -*- coding: utf-8 -*- from ast import Module from typing import Generator from flake8.options.manager import OptionManager from wemake_python_styleguide import constants from wemake_python_styleguide.options.config import Configuration from wemake_python_styleguide.types import ( CheckerSequence, CheckResult, ConfigurationOptions, ) from wemake_python_styleguide.version import version from wemake_python_styleguide.visitors.ast.complexity.counts import ( MethodMembersVisitor, ModuleMembersVisitor, ) from wemake_python_styleguide.visitors.ast.complexity.function import ( FunctionComplexityVisitor, ) from wemake_python_styleguide.visitors.ast.complexity.nested import ( NestedComplexityVisitor, ) from wemake_python_styleguide.visitors.ast.complexity.offset import ( OffsetVisitor, ) from wemake_python_styleguide.visitors.ast.wrong_class import WrongClassVisitor from wemake_python_styleguide.visitors.ast.wrong_contents import ( WrongContentsVisitor, ) from wemake_python_styleguide.visitors.ast.wrong_function_call import ( WrongFunctionCallVisitor, ) from wemake_python_styleguide.visitors.ast.wrong_import import ( WrongImportVisitor, ) from wemake_python_styleguide.visitors.ast.wrong_keyword import ( WrongKeywordVisitor, WrongRaiseVisitor, ) from wemake_python_styleguide.visitors.ast.wrong_name import ( WrongModuleMetadataVisitor, WrongNameVisitor, ) from wemake_python_styleguide.visitors.ast.wrong_string import ( WrongStringVisitor, ) from wemake_python_styleguide.visitors.filenames.wrong_module_name import ( WrongModuleNameVisitor, ) #: Visitors that should be working by default: ENABLED_VISITORS: CheckerSequence = [ # Styling and correctness: WrongRaiseVisitor, WrongFunctionCallVisitor, WrongImportVisitor, WrongKeywordVisitor, WrongNameVisitor, WrongModuleMetadataVisitor, WrongClassVisitor, WrongStringVisitor, WrongContentsVisitor, # Complexity: FunctionComplexityVisitor, NestedComplexityVisitor, OffsetVisitor, ModuleMembersVisitor, MethodMembersVisitor, # Modules: WrongModuleNameVisitor, ] 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 = constants.STDIN) -> None: """Creates new checker instance.""" self.tree = tree self.filename = filename @classmethod def add_options(cls, parser: OptionManager) -> None: """Calls Configuration instance method for registering options.""" cls.config.register_options(parser) @classmethod def parse_options(cls, options: ConfigurationOptions) -> None: """Parses registered options for providing to the visitor.""" 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: visitor = visitor_class( self.options, tree=self.tree, filename=self.filename, ) visitor.run() for error in visitor.errors: yield (*error.node_items(), type(self)) 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 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', 'obj', 'objects', 'objs', 'foo', 'bar', 'baz', ) 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(( # 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 = 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', )) #: List of blacklisted module names: BAD_MODULE_NAMES = frozenset(( 'util', 'utils', 'utilities', 'helpers', )) #: List of allowed module magic names: MAGIC_MODULE_NAMES_WHITELIST = frozenset(( '__init__', '__main__', )) # Internal variables # They are not publicly documented since they are only used internally # This variable is used as a default filename, when it is not passed by flake8: STDIN = 'stdin' # This variable is used to specify as a placeholder for `__init__.py`: INIT = '__init__' PK!uh+wemake_python_styleguide/errors/__init__.py# -*- coding: utf-8 -*- PK!v 'wemake_python_styleguide/errors/base.py# -*- coding: utf-8 -*- import ast from typing import Optional, 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_template`` and ``code`` fields. """ error_template: str code: str should_use_text: bool = True def __init__(self, node: Optional[ast.AST], text: str = None) -> None: """Creates new instance of AST style violation.""" self._node = node if text is None: self._text = node.__class__.__name__.lower() else: self._text = text def message(self) -> str: """Returns error's formatted message.""" if self.should_use_text: return self.error_template.format(self.code, self._text) return self.error_template.format(self.code) def node_items(self) -> Tuple[int, int, str]: """Returns `Tuple` to match `flake8` API format.""" line_number = getattr(self._node, 'lineno', 0) column_offset = getattr(self._node, 'col_offset', 0) return line_number, column_offset, self.message() class ASTStyleViolation(BaseStyleViolation): """AST based style violations.""" def __init__(self, node: ast.AST, text: str = None) -> None: """Creates new instance of AST style violation.""" super().__init__(node, text=text) class SimpleStyleViolation(BaseStyleViolation): """Style violation for cases where there's no AST nodes.""" def __init__(self, node=None, text: str = None) -> None: """Creates new instance of simple style violation.""" super().__init__(node, text=text) PK!ȍ*wemake_python_styleguide/errors/classes.py# -*- coding: utf-8 -*- """ These rules checks that ``class`` definitions are correct. Note: Beautiful is better than ugly. Explicit is better than implicit. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. """ from wemake_python_styleguide.errors.base import ASTStyleViolation class StaticMethodViolation(ASTStyleViolation): """ This rule forbids to use ``@staticmethod`` decorator. Use regular methods, ``classmethods`` or raw functions instead. Note: Returns Z300 as error code """ should_use_text = False error_template = '{0} Found using `@staticmethod`' code = 'Z300' class BadMagicMethodViolation(ASTStyleViolation): """ This rule forbids to use some magic methods. See ``BAD_MAGIC_METHODS`` for the full blacklist of the magic methods. Note: Returns Z301 as error code """ error_template = '{0} Found using restricted magic method "{1}"' code = 'Z301' class RequiredBaseClassViolation(ASTStyleViolation): """ This rule forbids to write classes without base classes. Example:: # Correct: class Some(object): ... # Wrong: class Some: ... Note: Returns Z302 as error code """ error_template = '{0} Found class without a base class "{1}"' code = 'Z302' PK!XF---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 Note: Simple is better than complex. Complex is better than complicated. Flat is better than nested. """ from wemake_python_styleguide.errors.base import ASTStyleViolation class NestedFunctionViolation(ASTStyleViolation): """ 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``. Example:: # Correct: def do_some(): ... def other(): ... # Wrong: def do_some(): def inner(): ... Note: Returns Z200 as error code """ error_template = '{0} Found nested function "{1}"' code = 'Z200' class NestedClassViolation(ASTStyleViolation): """ 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``. See ``NESTED_CLASSES_WHITELIST`` for the full list of names. Example:: # Correct: class Some(object): ... class Other(object): ... # Wrong: class Some(object): class Inner(object): ... Note: Returns Z201 as error code """ error_template = '{0} Found nested class "{1}"' code = 'Z201' class TooManyLocalsViolation(ASTStyleViolation): """ 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. This rule is configurable with ``--max-local-variables``. Note: Returns Z202 as error code """ error_template = '{0} Found too many local variables "{1}"' code = 'Z202' class TooManyArgumentsViolation(ASTStyleViolation): """ This rule forbids to have too many arguments for a function or method. This is an indicator of a bad desing. When function requires many arguments it shows that it is required to refactor this piece of code. This rule is configurable with ``--max-arguments``. Note: Returns Z203 as error code """ error_template = '{0} Found too many arguments "{1}"' code = 'Z203' class TooManyElifsViolation(ASTStyleViolation): """ This rule forbids to use many ``elif`` branches. This rule is specifically important, because many ``elif`` branches indicate a complex flow in your design: you are reimplementing ``switch`` in python. There are different design patters to use instead. This rule is configurable with ``--max-elifs``. Note: Returns Z204 as error code """ error_template = '{0} Found too many "{1}" branches' code = 'Z204' class TooManyReturnsViolation(ASTStyleViolation): """ 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. This rule is configurable with ``--max-returns``. Note: Returns Z205 as error code """ error_template = '{0} Found too many return statements "{1}"' code = 'Z205' class TooManyExpressionsViolation(ASTStyleViolation): """ 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. This rule is configurable with ``--max-expressions``. Note: Returns Z206 as error code """ error_template = '{0} Found too many expressions "{1}"' code = 'Z206' class TooDeepNestingViolation(ASTStyleViolation): """ 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. This rule is configurable with ``--max-offset-blocks``. Note: Returns Z207 as error code """ error_template = '{0} Found too deep nesting "{1}"' code = 'Z207' class TooManyModuleMembersViolation(ASTStyleViolation): """ This rule forbids to have many classes and functions in a single module. Having many classes and functions in a single module is a bad thing. Because soon it will be hard to read this code and understand it. 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 no care about functions and classes been public or not. However, methods are counted separately on a per-class basis. This rule is configurable with ``--max-module-members``. Note: Returns Z208 as error code """ error_template = '{0} Found too many members "{1}"' code = 'Z208' class TooManyMethodsViolation(ASTStyleViolation): """ This rule forbids to have many methods in a single class. We do not make any difference between instance and class methods. We also do no care about functions and classes been public or not. What to do if you have too many methods in a single class? Split this class in 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 This rule do not count attributes of a class. This rule is configurable with ``--max-methods``. Note: Returns Z209 as error code """ error_template = '{0} Found too many methods "{1}"' code = 'Z209' PK!css*wemake_python_styleguide/errors/general.py# -*- coding: utf-8 -*- """ These rules check some general issues. Like: 1. Incorrect naming 2. Using wrong builtins 3. Using wrong keywords 4. Working with exceptions "the bad way" Note: Beautiful is better than ugly. Explicit is better than implicit. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. """ from wemake_python_styleguide.errors.base import ASTStyleViolation class WrongKeywordViolation(ASTStyleViolation): """ This rule forbids to use some keywords from ``python``. We do this, since some keywords are anti-patterns. Example:: # Wrong: pass del nonlocal global Note: Returns Z110 as error code """ error_template = '{0} Found wrong keyword "{1}"' code = 'Z110' class BareRiseViolation(ASTStyleViolation): """ 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_template = '{0} Found bare raise outside of except "{1}"' code = 'Z111' class RaiseNotImplementedViolation(ASTStyleViolation): """ 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_template = '{0} Found raise NotImplemented "{1}"' code = 'Z112' class WrongFunctionCallViolation(ASTStyleViolation): """ 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. See ``BAD_FUNCTIONS`` for the full list of blacklisted functions. Note: Returns Z113 as error code """ error_template = '{0} Found wrong function call "{1}"' code = 'Z113' class WrongVariableNameViolation(ASTStyleViolation): """ This rule forbids to have blacklisted variable names. See ``BAD_VARIABLE_NAMES`` for the full list of blacklisted variable names. Example:: # Correct: html_node = None # Wrong: item = None Note: Returns Z114 as error code """ error_template = '{0} Found wrong variable name "{1}"' code = 'Z114' class TooShortVariableNameViolation(ASTStyleViolation): """ This rule forbids to have too short variable names. This rule is configurable with ``--min-variable-length``. Example:: # Correct: x_coord = 1 # Wrong: x = 1 Note: Returns Z115 as error code """ error_template = '{0} Found too short name "{1}"' code = 'Z115' class PrivateNameViolation(ASTStyleViolation): """ 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_template = '{0} Found private name pattern "{1}"' code = 'Z116' class WrongModuleMetadataViolation(ASTStyleViolation): """ 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. See ``BAD_MODULE_METADATA_VARIABLES`` for full list of bad names. Example:: # Wrong: __author__ = 'Nikita Sobolev' Note: Returns Z117 as error code """ error_template = '{0} Found wrong metadata variable {1}' code = 'Z117' class FormattedStringViolation(ASTStyleViolation): """ This rule forbids to use ``f`` strings. ``f`` strings looses context too often and they are hard to lint. Also, they promote a bad practice: putting your logic inside the template. Use ``.format()`` instead. Example:: # Wrong: f'Result is: {2 + 2}' # Correct: 'Result is: {0}'.format(2 + 2) Note: Returns Z118 as error code """ should_use_text = False error_template = '{0} Found `f` string' code = 'Z118' class EmptyModuleViolation(ASTStyleViolation): """ This rule forbids to have empty modules. If you have an empty module there are two ways to handle that: 1. delete it, why is it even there? 2. drop some documentation in it, so you will explain why it is there Note: Returns Z119 as error code """ should_use_text = False error_template = '{0} Found empty module' code = 'Z119' class InitModuleHasLogicViolation(ASTStyleViolation): """ This rule forbids to have logic inside ``__init__`` module. 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 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 Note: Returns Z120 as error code """ should_use_text = False error_template = '{0} Found `__init__` module with logic' code = 'Z120' PK!mZ  *wemake_python_styleguide/errors/imports.py# -*- coding: utf-8 -*- """ These rules checks ``import`` statements to be defined correctly. Note: Explicit is better than implicit. Flat is better than nested. Sparse is better than dense. Readability counts. """ from wemake_python_styleguide.errors.base import ASTStyleViolation class LocalFolderImportViolation(ASTStyleViolation): """ 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_template = '{0} Found local folder import "{1}"' code = 'Z100' class NestedImportViolation(ASTStyleViolation): """ 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:: # Correct: from my_module import some_function def some(): ... # Wrong: def some(): from my_module import some_function Note: Returns Z101 as error code """ error_template = '{0} Found nested import "{1}"' code = 'Z101' class FutureImportViolation(ASTStyleViolation): """ 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. See ``FUTURE_IMPORTS_WHITELIST`` for the full list of allowed future imports. Example:: # Correct: from __future__ import annotations # Wrong: from __future__ import print_function Note: Returns Z102 as error code """ error_template = '{0} Found future import "{1}"' code = 'Z102' class DottedRawImportViolation(ASTStyleViolation): """ 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_template = '{0} Found dotted raw import "{1}"' code = 'Z103' class SameAliasImportViolation(ASTStyleViolation): """ This rule forbids to use the same alias as the original name in imports. Why would you even do this in the first place? However, sometimes we see this error in the real code. Example:: # Correct: from os import path # Wrong: from os import path as path Note: Returns Z104 as error code """ error_template = '{0} Found same alias import "{1}"' code = 'Z104' PK!}7Z*wemake_python_styleguide/errors/modules.py# -*- coding: utf-8 -*- """ These rules checks that modules are defined correctly. Please, take a note that these rules are not applied to packages. Things we check here: 1. Naming 2. Contents: some modules must have contents, some must not """ from wemake_python_styleguide.errors.base import SimpleStyleViolation class WrongModuleNameViolation(SimpleStyleViolation): """ This rule forbids to use blacklisted module names. See ``BAD_MODULE_NAMES`` for the full list of bad module names. Example:: # Correct: github.py views.py # Wrong: utils.py helpers.py Note: Returns Z400 as error code """ should_use_text = False error_template = '{0} Found wrong module name' code = 'Z400' class WrongModuleMagicNameViolation(SimpleStyleViolation): """ This rule forbids to use any magic names except whitelisted ones. See ``MAGIC_MODULE_NAMES_WHITELIST`` for the full list of allowed magic module names. Example:: # Correct: __init__.py __main__.py # Wrong: __version__.py Note: Returns Z401 as error code """ should_use_text = False error_template = '{0} Found wrong module magic name' code = 'Z401' class TooShortModuleNameViolation(SimpleStyleViolation): """ This rule forbids to use module name shorter than some breakpoint. Too short module names are not expressive enough. We will have to open the source code to find out what is going on there. This rule is configurable with ``--min-module-name-length``. Note: Returns Z402 as error code """ should_use_text = False error_template = '{0} Found too short module name' code = 'Z402' PK!uh+wemake_python_styleguide/logics/__init__.py# -*- coding: utf-8 -*- PK!RYFWW,wemake_python_styleguide/logics/filenames.py# -*- coding: utf-8 -*- from pathlib import PurePath from typing import Iterable from wemake_python_styleguide.options.defaults import MIN_MODULE_NAME_LENGTH 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 either 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 either 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 = MIN_MODULE_NAME_LENGTH, ) -> bool: """ Checks either 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 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 >>> is_method('') False """ return function_type in ['method', 'classmethod'] PK!\rr*wemake_python_styleguide/logics/imports.py# -*- coding: utf-8 -*- import ast 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 '.' 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!aZ,wemake_python_styleguide/logics/variables.py# -*- coding: utf-8 -*- from typing import Iterable, Optional from wemake_python_styleguide.options.defaults import MIN_VARIABLE_LENGTH 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 = MIN_VARIABLE_LENGTH, ) -> 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!}zz*wemake_python_styleguide/options/config.py# -*- coding: utf-8 -*- from typing import Dict, Sequence, Union import attr from flake8.options.manager import OptionManager from wemake_python_styleguide.options import defaults ConfigValues = Dict[str, Union[str, int, bool]] @attr.attrs(frozen=True, auto_attribs=True, slots=True) class Option(object): """This class represent `flake8` option object.""" long_option_name: str default: int # noqa: E704 help: str type: str = 'int' # noqa: A003 parse_from_config: bool = True 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`` - `max-offset-blocks` - maximum number of block to nest expressions, defaults to ``MAX_OFFSET_BLOCKS`` - `max-elifs` - maximum number of `elif` blocks, defaults to ``MAX_ELIFS`` - `max-module-members` - maximum number of classes and functions in a single module, defaults to ``MAX_MODULE_MEMBERS`` - `max-methods` - maximum number of methods in a single class, defaults to ``MAX_METHODS`` - `min-module-name-length` - minimum required module's name length, defaults to ``MIN_MODULE_NAME_LENGTH`` """ def _all_options(self) -> Sequence[Option]: return [ 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( '--min-variable-length', defaults.MIN_VARIABLE_LENGTH, 'Minimum required length of the variable name.', ), 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( '--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.""" options = self._all_options() for option in options: parser.add_option(**attr.asdict(option)) PK!=  ,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. """ #: 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 #: Maximum number of blocks to nest different structures: MAX_OFFSET_BLOCKS = 5 #: Maximum number of `elif` blocks in a single `if` condition: MAX_ELIFS = 2 #: Maximum number of classes and functions in a single module: MAX_MODULE_MEMBERS = 7 #: Maximum number of methods in a single class: MAX_METHODS = 7 # Modules #: Minimum required module's name length: MIN_MODULE_NAME_LENGTH = 3 PK!F+!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 TYPE_CHECKING, Sequence, Tuple, Type, Union if TYPE_CHECKING: # pragma: no cover from typing_extensions import Protocol # noqa: Z101 # This solves cycle imports problem: from .visitors import base # noqa: Z100,Z101,F401 else: # We do not need to do anything if typechecker is not working: Protocol = object #: Checkers container, that has all enabled visitors' classes: CheckerSequence = Sequence[Type['base.BaseChecker']] #: 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] #: Code members that we count in a module: ModuleMembers = Union[ast.FunctionDef, ast.ClassDef] 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 max_offset_blocks: int max_elifs: int max_module_members: int max_methods: int # Modules: min_module_name_length: int PK!Z#wemake_python_styleguide/version.py# -*- coding: utf-8 -*- import pkg_resources #: We store the version number inside the `pyproject.toml`: version: str = pkg_resources.get_distribution( 'wemake-python-styleguide', ).version PK!uh-wemake_python_styleguide/visitors/__init__.py# -*- coding: utf-8 -*- PK!uh1wemake_python_styleguide/visitors/ast/__init__.py# -*- coding: utf-8 -*- PK!uh<wemake_python_styleguide/visitors/ast/complexity/__init__.py# -*- coding: utf-8 -*- PK! X :wemake_python_styleguide/visitors/ast/complexity/counts.py# -*- coding: utf-8 -*- import ast from collections import defaultdict from typing import DefaultDict from wemake_python_styleguide.errors.complexity import ( TooManyMethodsViolation, TooManyModuleMembersViolation, ) 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 ModuleMembers from wemake_python_styleguide.visitors.base import BaseNodeVisitor 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 max_members = self.options.max_module_members if has_just_exceeded_limit(self._public_items_count, max_members): self.add_error( TooManyModuleMembersViolation(node, text=self.filename), ) def visit_ClassDef(self, node: ast.ClassDef) -> None: """ Counts the number of `class`es in a single module. Raises: TooManyModuleMembersViolation """ self._check_members_count(node) self.generic_visit(node) def visit_FunctionDef(self, node: ast.FunctionDef) -> None: """ Counts the number of functions in a single module. Raises: TooManyModuleMembersViolation """ self._check_members_count(node) self.generic_visit(node) 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: ast.FunctionDef) -> None: parent = getattr(node, 'parent', None) if isinstance(parent, ast.ClassDef): self._methods[parent] += 1 max_methods = self.options.max_methods if has_just_exceeded_limit(self._methods[parent], max_methods): self.add_error(TooManyMethodsViolation(node, text=parent.name)) def visit_FunctionDef(self, node: ast.FunctionDef) -> None: """ Counts the number of methods in a single class. Raises: TooManyMethodsViolation """ self._check_method(node) self.generic_visit(node) PK!::<wemake_python_styleguide/visitors/ast/complexity/function.py# -*- coding: utf-8 -*- import ast from collections import defaultdict from typing import DefaultDict, List, Type from wemake_python_styleguide.errors.base import BaseStyleViolation from wemake_python_styleguide.errors.complexity import ( TooManyArgumentsViolation, TooManyElifsViolation, 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.visitors.base import BaseNodeVisitor class _ComplexityCounter(object): """Helper class to encapsulate logics from the visitor.""" def __init__(self, delegate: 'FunctionComplexityVisitor') -> None: self.delegate = delegate self.expressions: DefaultDict[str, int] = defaultdict(int) self.variables: DefaultDict[str, List[str]] = defaultdict(list) self.returns: DefaultDict[str, int] = defaultdict(int) def _update_variables( self, function: ast.FunctionDef, variable_name: str, ) -> None: """ Increases the counter of local variables. What is treated as a local variable? Check ``TooManyLocalsViolation`` documentation. """ 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), self.delegate.options.max_local_variables, ) if limit_exceeded: self.delegate.add_error( TooManyLocalsViolation(function, text=function.name), ) def _update_counter( self, function: ast.FunctionDef, counter: DefaultDict[str, int], max_value: int, exception: Type[BaseStyleViolation], ) -> None: counter[function.name] += 1 limit_exceeded = has_just_exceeded_limit( counter[function.name], max_value, ) if limit_exceeded: self.delegate.add_error(exception(function, text=function.name)) def _update_elifs(self, node: ast.If, count: int = 0) -> None: if node.orelse and isinstance(node.orelse[0], ast.If): self._update_elifs(node.orelse[0], count=count + 1) else: if count > self.delegate.options.max_elifs: self.delegate.add_error(TooManyElifsViolation(node)) def _check_sub_node(self, node: ast.FunctionDef, 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, getattr(sub_node, 'id')) if isinstance(sub_node, ast.Return): self._update_counter( node, self.returns, self.delegate.options.max_returns, TooManyReturnsViolation, ) if isinstance(sub_node, ast.Expr): self._update_counter( node, self.expressions, self.delegate.options.max_expressions, TooManyExpressionsViolation, ) if isinstance(sub_node, ast.If): self._update_elifs(sub_node) def check_arguments_count(self, node: ast.FunctionDef) -> 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 if counter > self.delegate.options.max_arguments + has_extra_arg: self.delegate.add_error( TooManyArgumentsViolation(node, text=node.name), ) def check_function_complexity(self, node: ast.FunctionDef) -> 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) 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 5. Number of `elif`s """ def __init__(self, *args, **kwargs) -> None: """Creates a counter for tracked metrics.""" super().__init__(*args, **kwargs) self._counter = _ComplexityCounter(self) def visit_FunctionDef(self, node: ast.FunctionDef) -> 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) PK!vqA> > :wemake_python_styleguide/visitors/ast/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 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) -> None: """ 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) -> None: """ 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) -> None: """ 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!{(S:wemake_python_styleguide/visitors/ast/complexity/offset.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.errors.complexity import TooDeepNestingViolation from wemake_python_styleguide.visitors.base import BaseNodeVisitor class OffsetVisitor(BaseNodeVisitor): """This visitor checks offset values for some nodes.""" def _check_offset(self, node: ast.AST) -> None: offset = getattr(node, 'col_offset', None) if offset is not None and offset > self.options.max_offset_blocks * 4: self.add_error(TooDeepNestingViolation(node)) def visit_Expr(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, `Name` node has inline offset, which can take values from 0 to ~80. But `Name` node is allowed to behave like so. Raises: TooDeepNestingViolation """ self._check_offset(node) self.generic_visit(node) visit_Try = visit_ExceptHandler = visit_Expr visit_For = visit_With = visit_While = visit_If = visit_Expr visit_Raise = visit_Return = visit_Continue = visit_Break = visit_Expr visit_Assign = visit_Expr PK!d^rRMM4wemake_python_styleguide/visitors/ast/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 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) -> None: for decorator in node.decorator_list: decorator_name = getattr(decorator, 'id', None) if decorator_name == 'staticmethod': self.add_error(StaticMethodViolation(node)) def _check_magic_methods(self, node: ast.FunctionDef) -> None: if node.name in BAD_MAGIC_METHODS: self.add_error(BadMagicMethodViolation(node, text=node.name)) def _check_base_class(self, node: ast.ClassDef) -> None: if len(node.bases) == 0: self.add_error(RequiredBaseClassViolation(node, text=node.name)) def visit_ClassDef(self, node: ast.ClassDef) -> None: """ 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) -> None: """ 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!Ko7wemake_python_styleguide/visitors/ast/wrong_contents.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.constants import INIT from wemake_python_styleguide.errors.general import ( EmptyModuleViolation, InitModuleHasLogicViolation, ) from wemake_python_styleguide.logics.filenames import is_stem_in_list from wemake_python_styleguide.visitors.base import BaseNodeVisitor class WrongContentsVisitor(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: 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_error(EmptyModuleViolation(node)) def _check_init_contents(self, node: ast.Module) -> None: if not self._is_init() or not node.body: return if len(node.body) > 1: self.add_error(InitModuleHasLogicViolation(node)) return if not self._is_doc_string(node.body[0]): self.add_error(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 not be empty. Raises: EmptyModuleViolation InitModuleHasLogicViolation """ self._check_init_contents(node) self._check_module_contents(node) self.generic_visit(node) PK!<wemake_python_styleguide/visitors/ast/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 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) -> None: """ 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!cV 5wemake_python_styleguide/visitors/ast/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.logics.imports import get_error_text from wemake_python_styleguide.types import AnyImport from wemake_python_styleguide.visitors.base import BaseNodeVisitor class _ImportsChecker(object): def __init__(self, delegate: 'WrongImportVisitor') -> None: self.delegate = delegate def check_nested_import(self, node: AnyImport) -> None: text = get_error_text(node) parent = getattr(node, 'parent', None) if not isinstance(parent, ast.Module): self.delegate.add_error(NestedImportViolation(node, text=text)) def check_local_import(self, node: ast.ImportFrom) -> None: text = get_error_text(node) if node.level != 0: self.delegate.add_error( 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.delegate.add_error( 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.delegate.add_error( DottedRawImportViolation(node, text=alias.name), ) def check_alias(self, node: AnyImport) -> None: for alias in node.names: if alias.asname == alias.name: self.delegate.add_error( SameAliasImportViolation(node, text=alias.name), ) class WrongImportVisitor(BaseNodeVisitor): """This class is 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) 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.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.generic_visit(node) PK!} 6wemake_python_styleguide/visitors/ast/wrong_keyword.py# -*- coding: utf-8 -*- import ast from wemake_python_styleguide.errors.general import ( BareRiseViolation, RaiseNotImplementedViolation, WrongKeywordViolation, ) from wemake_python_styleguide.visitors.base 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) -> None: """ Used to find `global` keyword. Raises: WrongKeywordViolation """ self.add_error(WrongKeywordViolation(node)) self.generic_visit(node) def visit_Nonlocal(self, node: ast.Nonlocal) -> None: """ Used to find `nonlocal` keyword. Raises: WrongKeywordViolation """ self.add_error(WrongKeywordViolation(node)) self.generic_visit(node) def visit_Delete(self, node: ast.Delete) -> None: """ 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) -> None: """ Used to find `pass` keyword. Raises: WrongKeywordViolation """ self.add_error(WrongKeywordViolation(node)) self.generic_visit(node) PK!.3wemake_python_styleguide/visitors/ast/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 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, name: str) -> None: if is_wrong_variable_name(name, BAD_VARIABLE_NAMES): self.add_error(WrongVariableNameViolation(node, text=name)) min_length = self.options.min_variable_length if is_too_short_variable_name(name, min_length=min_length): self.add_error(TooShortVariableNameViolation(node, text=name)) if is_private_variable(name): self.add_error(PrivateNameViolation(node, text=name)) 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) -> None: """ Used to find wrong attribute names inside classes. Raises: WrongVariableNameViolation TooShortVariableNameViolation PrivateNameViolation """ if isinstance(node.ctx, ast.Store): self._check_name(node, node.attr) self.generic_visit(node) def visit_FunctionDef(self, node: ast.FunctionDef) -> 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_ExceptHandler(self, node: ast.ExceptHandler) -> None: """ Used to find wrong exception instances in ``try``/``except``. Raises: WrongVariableNameViolation TooShortVariableNameViolation PrivateNameViolation """ self._check_name(node, getattr(node, 'name', None)) self.generic_visit(node) def visit_Name(self, node: ast.Name) -> None: """ Used to find wrong regular variables. Raises: WrongVariableNameViolation TooShortVariableNameViolation PrivateNameViolation """ if isinstance(node.ctx, ast.Store): self._check_name(node, node.id) self.generic_visit(node) def visit_Import(self, node: AnyImport) -> None: """ 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) -> 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 BAD_MODULE_METADATA_VARIABLES: self.add_error( 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) PK!ײ5wemake_python_styleguide/visitors/ast/wrong_string.py# -*- coding: utf-8 -*- from wemake_python_styleguide.errors.general import FormattedStringViolation from wemake_python_styleguide.visitors.base import BaseNodeVisitor class WrongStringVisitor(BaseNodeVisitor): """Restricts to use `f` strings.""" def visit_JoinedStr(self, node) -> None: # type is not defined in ast yet """ Restricts to use `f` strings. Raises: FormattedStringViolation """ self.add_error(FormattedStringViolation(node)) self.generic_visit(node) PK!"K@ @ )wemake_python_styleguide/visitors/base.py# -*- coding: utf-8 -*- import ast from typing import List from wemake_python_styleguide import constants from wemake_python_styleguide.errors.base import BaseStyleViolation from wemake_python_styleguide.types import ConfigurationOptions class BaseChecker(object): """ Base class for different type of checkers. Attributes: tree: AST tree to be checked if any. options: contains the options objects passed and parsed by `flake8`. filename: filename passed by `flake8`. errors: list of errors for the specific checker. """ def __init__( self, options: ConfigurationOptions, tree: ast.AST = None, filename: str = 'stdin', ) -> None: """Creates new instance.""" super().__init__() self.options = options self.tree = tree self.filename = filename self.errors: List[BaseStyleViolation] = [] def add_error(self, error: BaseStyleViolation) -> None: """Adds error to the visitor.""" self.errors.append(error) def run(self) -> None: """This method should be defined in all subclasses of this class.""" raise NotImplementedError('Should be defined in a subclass') class BaseNodeVisitor(ast.NodeVisitor, BaseChecker): """ This class allows to store errors 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. """ def run(self) -> None: """Runs `visit()` method of `NodeVisitor` with the correct params.""" if self.tree is None: raise ValueError('Parsing without a defined trie') self.visit(self.tree) class BaseFilenameVisitor(BaseChecker): """ This class allows to check module filenames. Method `visit()` is used only for API compatibility. """ def visit_filename(self) -> None: """This method should be overridden in a subclass.""" raise NotImplementedError('Should be defined in a subclass') def run(self) -> None: """ Checks module's filename. If filename equals to ``STDIN`` constant then this check is ignored. Otherwise, runs `visit_filename()` method. """ if self.filename != constants.STDIN: self.visit_filename() PK!uh7wemake_python_styleguide/visitors/filenames/__init__.py# -*- coding: utf-8 -*- PK!u'uxx@wemake_python_styleguide/visitors/filenames/wrong_module_name.py# -*- coding: utf-8 -*- from wemake_python_styleguide.constants import ( BAD_MODULE_NAMES, MAGIC_MODULE_NAMES_WHITELIST, ) from wemake_python_styleguide.errors.modules import ( TooShortModuleNameViolation, WrongModuleMagicNameViolation, WrongModuleNameViolation, ) from wemake_python_styleguide.logics.filenames import ( is_magic, is_stem_in_list, is_too_short_stem, ) from wemake_python_styleguide.visitors.base import BaseFilenameVisitor class WrongModuleNameVisitor(BaseFilenameVisitor): """Checks that modules have correct names.""" def _check_module_name(self) -> None: if is_stem_in_list(self.filename, BAD_MODULE_NAMES): self.add_error(WrongModuleNameViolation()) def _check_magic_name(self) -> None: if is_magic(self.filename): good_magic = is_stem_in_list( self.filename, MAGIC_MODULE_NAMES_WHITELIST, ) if not good_magic: self.add_error(WrongModuleMagicNameViolation()) def _check_module_name_length(self) -> None: is_short = is_too_short_stem( self.filename, min_length=self.options.min_module_name_length, ) if is_short: self.add_error(TooShortModuleNameViolation()) def visit_filename(self) -> None: """ Checks a single module's filename. Raises: TooShortModuleNameViolation WrongModuleMagicNameViolation WrongModuleNameViolation """ self._check_module_name() self._check_magic_name() self._check_module_name_length() PK!H";?:wemake_python_styleguide-0.0.11.dist-info/entry_points.txtNINK(I+ϋ劲-O TdT椦f%g&gY9Ch..PK!f001wemake_python_styleguide-0.0.11.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.11.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]n0H*J>mlcAPK!H?L(2wemake_python_styleguide-0.0.11.dist-info/METADATAWS۸ߟB;6vBh 9!!v "JrBH6WvuuѹWWWLӈjgRqwHpF4cd3XDҫ%ն嵼 gRf9OɄD<,")5-lRM=䂇L9oEO4DBu͘ Pd͚ɵuQfG@;Z 0nia˘s>\2-k9*guZXDS]*M?-zǶ/QO $…΍.tˈ:ĶtCOƒYYePם}B ]T힯 Vͮt~e8?'O}WduyY쩄4R̀F1kX2{j4`Z{*bkMgkAk݀*ZwOΖw3L4S|O{/NߜA=}`U=5_:y{27/ijzfǞ}uwhpx2]g.Z3?:<凞?<9n, ܐ{B?|ې%hr4 Xhߤ" ᇁ{'? ԬbɢnT4f\/Iq<{_9-tLB={jvS|J?ȴo,+fyܳHsIO$5- -@8_+-l%bԳݵ~{MowBu*$|R4-cP1/#ZsX\ 5tE.KPrQ{=Ƕ$4%bHO 8$ 9!`yd gFAsZ !yqa#ˍcjgd̴;.6|VZf:9)H"!GyVydk6ӫ9C_~ GޙIAzASm72L`!!!C&~rQƉQ/@ϭzt A5nM+RڗRHE Qs@IO#4" `Ban(DfGUH6í̎{/q 45ckfum"C,p$r`4K2( &T_  m!)q-DХHٍFhzMO\B]Mby 6) p^U`'2V5b};N\˵*!V4lPL6 :/UU(`Ϟእ46e`)xAx5xkf11;QzdIF઀Rs!lT܄D4~Dr5*?<(%q&Oٰ;=,"hVg:T!BBkBا>Di0pN  uV"5^]7mae6^,·ubM+E#Vңa?=)k@H(dcӔqPK!H190wemake_python_styleguide-0.0.11.dist-info/RECORDŗۖY A. n XVR1?ךs2kX~K0鳐)W wv~[&h4,ZIW+&C1X15:@TirJ᪥>V-Jz*;*VhrBR,ꇰ&n8vw1oN~(9+9zݗȷwW%$Q`-j͢ w {#)qPGM0j_SOGe[9ls` v6,ھJR=tpSFDD}mJpDc%y"eڹb:d"iCoqBT 'O;T l pwB78nĂ\Ԇצ^ ٴGk[D+ e [|m B0:Wre \ 'ˀ~n 8qc wTWmDY0JH` ?Sص@o1N~ju["z-lV d>_bS=K 3}F^# `̌<#8AP?837펝$}peLnFsLrCw+W `R"2q*dq;2tv;UDX +bמ2N,!*m(bzD6.Ck|K㩺Ki#>b493 cU[*Iv[R1b1標{^!)bcJKtĕߋ8/3kbeOT;)ɏ4$wpW"K:I^(mYq]O#jBI\lD#r㔯ׂTZg {8Mÿ{)?Q=n&]ͱܜhf qX2S.dI> `WQ;K^>A0_4DSI>xSjEsV@АsӦYp neG ?L@xy8D$Y'I0qN14j&_ݽ{`JÓ 6S}Kᝦ 9uuէ'yMd7uo}i^O\ӭz4G24kkl-v(SyuD]7 W^խf7M&m''fku5ދe(,U e~p1BLzdTf+r21%?旍%'Vf8MY73`(./,$c3nk6ⱚkBVלqZ'!mӿe؜iNX&4A(#Nj;~rcW,R1{a٦&ټ!}~ooM_ i_K^"SZdN'O2(~լ].T#PUQ3N1)Ag(DPFkEplMɑEQ}$/{ߣ5.j¼Q8441h6'aVGA gq/ ]Z%[ ~;1>lY PK!uh$wemake_python_styleguide/__init__.pyPK!5#Zwemake_python_styleguide/checker.pyPK!;P+ + %wemake_python_styleguide/constants.pyPK!uh+wemake_python_styleguide/errors/__init__.pyPK!v 'pwemake_python_styleguide/errors/base.pyPK!ȍ* wemake_python_styleguide/errors/classes.pyPK!XF---a&wemake_python_styleguide/errors/complexity.pyPK!css*Awemake_python_styleguide/errors/general.pyPK!mZ  *Ywemake_python_styleguide/errors/imports.pyPK!}7Z*dwemake_python_styleguide/errors/modules.pyPK!uh+'lwemake_python_styleguide/logics/__init__.pyPK!RYFWW,lwemake_python_styleguide/logics/filenames.pyPK![Һ,)swemake_python_styleguide/logics/functions.pyPK!\rr* > :wemake_python_styleguide/visitors/ast/complexity/nested.pyPK!{(S:wemake_python_styleguide/visitors/ast/complexity/offset.pyPK!d^rRMM4wemake_python_styleguide/visitors/ast/wrong_class.pyPK!Ko7Uwemake_python_styleguide/visitors/ast/wrong_contents.pyPK!<wemake_python_styleguide/visitors/ast/wrong_function_call.pyPK!cV 5wemake_python_styleguide/visitors/ast/wrong_import.pyPK!} 6wemake_python_styleguide/visitors/ast/wrong_keyword.pyPK!.3wemake_python_styleguide/visitors/ast/wrong_name.pyPK!ײ5 wemake_python_styleguide/visitors/ast/wrong_string.pyPK!"K@ @ )ywemake_python_styleguide/visitors/base.pyPK!uh7wemake_python_styleguide/visitors/filenames/__init__.pyPK!u'uxx@mwemake_python_styleguide/visitors/filenames/wrong_module_name.pyPK!H";?:C"wemake_python_styleguide-0.0.11.dist-info/entry_points.txtPK!f001"wemake_python_styleguide-0.0.11.dist-info/LICENSEPK!H_zTT/U'wemake_python_styleguide-0.0.11.dist-info/WHEELPK!H?L(2'wemake_python_styleguide-0.0.11.dist-info/METADATAPK!H190/wemake_python_styleguide-0.0.11.dist-info/RECORDPK++6