Source code for wemake_python_styleguide.violations.base
"""Contains detailed technical information about :term:`violation` internals... _violations:Violations API--------------.. currentmodule:: wemake_python_styleguide.violations.base.. autoclasstree:: wemake_python_styleguide.violations.base.. autosummary:: :nosignatures: ASTViolation MaybeASTViolation TokenizeViolation SimpleViolationViolation cannot have more than one base class.See :ref:`tutorial` for more information about choosing a correct base class.Conventions~~~~~~~~~~~- Each violation class name should end with "Violation"- Each violation must have a long docstring with full description- Each violation must have "Reasoning" and "Solution" sections- Each violation must have "versionadded" policy- Each violation should have an example with correct and wrong usages- If violation error template should have a parameter it should be the last part of the text: ``: {0}``Deprecating a violation~~~~~~~~~~~~~~~~~~~~~~~When you want to mark some violation as deprecated and disabled,then assign ``disabled_since`` with a string version number to it:.. code:: python @final class SomeViolation(ASTViolation): disabled_since = '1.0.0'Reference~~~~~~~~~"""importabcimportastimportenumimporttokenizefromcollections.abcimportCallablefromtypingimportClassVar,TypeAlias,final#: General type for all possible nodes where error happens.ErrorNode:TypeAlias=ast.AST|tokenize.TokenInfo|None#: We use this type to define helper classes with callbacks to add violations.ErrorCallback:TypeAlias=Callable[['BaseViolation'],None]
[docs]@enum.uniqueclassViolationPostfixes(enum.Enum):"""String values of postfixes used for violation baselines."""bigger_than=' > {0}'less_than=' < {0}'
[docs]classBaseViolation(abc.ABC):""" Abstract base class for all style violations. It basically just defines how to create any error and how to format this error later on. Each subclass must define ``error_template`` and ``code`` fields. """error_template:ClassVar[str]code:ClassVar[int]disabled_since:ClassVar[str|None]=None# assigned in __init_subclass__full_code:ClassVar[str]summary:ClassVar[str]# We use this code to show base metrics and thresholds mostly:postfix_template:ClassVar[ViolationPostfixes]=(ViolationPostfixes.bigger_than)def__init_subclass__(cls,**kwargs)->None:"""Sets additional values for subclasses."""super().__init_subclass__(**kwargs)violation_code=getattr(cls,'code',None)ifviolation_codeisNone:returnifcls.__doc__isNone:raiseTypeError(f'Please include a docstring documenting {cls}',)# this is mostly done for docs to display the full code,# allowing its indexing in search engines and better discoverabilitycls.full_code=cls._full_code()cls.summary=cls.__doc__.lstrip().split('\n',maxsplit=1)[0]# this hack adds full code to summary table in the docscls.__doc__=_prepend_skipping_whitespaces(f'{cls.full_code} — ',cls.__doc__,)def__init__(self,node:ErrorNode,text:str|None=None,baseline:int|None=None,)->None:""" Creates a new instance of an abstract violation. Arguments: node: violation was raised by this node. If applicable. text: extra text to format the final message. If applicable. baseline: some complexity violations show the logic threshold here. """self._node=nodeself._text=textself._baseline=baseline
[docs]@finaldefmessage(self)->str:""" Returns error's formatted message with code and reason. Conditionally formats the ``error_template`` if it is required. """formatted=self.error_template.format(self._text)ifself._textandformatted==self.error_template:# pragma: no coverraiseValueError('Error message was not formatted',self)returnf'{self.full_code}{formatted}{self._postfix_information()}'
[docs]@finaldefnode_items(self)->tuple[int,int,str]:"""Returns tuple to match ``flake8`` API format."""return(*self._location(),self.message())
@final@classmethoddef_full_code(cls)->str:""" Returns fully formatted code. Adds violation letter to the numbers. Also ensures that codes like ``3`` will be represented as ``WPS003``. """code_part=str(cls.code).zfill(3)returnf'WPS{code_part}'@finaldef_postfix_information(self)->str:""" Adds useful information to the end of the violation message. Useful for complexity baselines and other thresholds. """ifself._baselineisNone:return''returnself.postfix_template.value.format(self._baseline)@abc.abstractmethoddef_location(self)->tuple[int,int]:"""Base method for showing error location."""
class_BaseASTViolation(BaseViolation):"""Used as a based type for all ``ast`` violations."""_node:ast.AST|None@finaldef_location(self)->tuple[int,int]:line_number=getattr(self._node,'lineno',0)column_offset=getattr(self._node,'col_offset',0)returnline_number,column_offset
[docs]classASTViolation(_BaseASTViolation):"""Violation for ``ast`` based style visitors."""_node:ast.AST
[docs]classMaybeASTViolation(_BaseASTViolation):""" Violation for ``ast`` and modules visitors. Is used for violations that share the same rule for nodes and module names. Is wildly used for naming rules. """def__init__(self,node:ast.AST|None=None,text:str|None=None,baseline:int|None=None,)->None:"""Creates new instance of module violation without explicit node."""super().__init__(node,text=text,baseline=baseline)
[docs]classTokenizeViolation(BaseViolation):"""Violation for ``tokenize`` based visitors."""_node:tokenize.TokenInfo@finaldef_location(self)->tuple[int,int]:returnself._node.start
[docs]classSimpleViolation(BaseViolation):"""Violation for cases where there's no associated nodes."""_node:Nonedef__init__(self,node=None,text:str|None=None,baseline:int|None=None,)->None:"""Creates new instance of simple style violation."""super().__init__(node,text=text,baseline=baseline)@finaldef_location(self)->tuple[int,int]:""" Return violation location inside the file. Default location is in the so-called "file beginning". Cannot be ignored by inline ``noqa`` comments. """return0,0