Best practices

These checks ensure that you follow the best practices.

The source for these best practices is countless hours we have spent debugging software or reviewing it.

How do we find inspiration for new rules? We find some ugly code during code reviews and audits, then we forbid the use of code like it forever.

Summary

WrongMagicCommentViolation

WPS400 — Restrict various control (such as magic) comments.

WrongDocCommentViolation

WPS401 — Forbid empty doc comments (#:).

OveruseOfNoqaCommentViolation

WPS402 — Forbid too many # noqa comments.

OveruseOfNoCoverCommentViolation

WPS403 — Forbid too many # pragma: no cover comments.

ComplexDefaultValueViolation

WPS404 — Forbid complex defaults.

LoopVariableDefinitionViolation

WPS405 — Forbid anything other than ast.Name to define loop variables.

ContextManagerVariableDefinitionViolation

WPS406 — Forbid anything other than ast.Name to define contexts.

MutableModuleConstantViolation

WPS407 — Forbid mutable constants on a module level.

SameElementsInConditionViolation

WPS408 — Forbid using the same logical conditions in one expression.

HeterogeneousCompareViolation

WPS409 — Forbid heterogeneous operators in one comparison.

WrongModuleMetadataViolation

WPS410 — Forbid some module-level variables.

EmptyModuleViolation

WPS411 — Forbid empty modules.

InitModuleHasLogicViolation

WPS412 — Forbid logic inside __init__ module.

BadMagicModuleFunctionViolation

WPS413 — Forbid __getattr__ and __dir__ module magic methods.

WrongUnpackingViolation

WPS414 — Forbid tuple unpacking with side-effects.

DuplicateExceptionViolation

WPS415 — Forbid the same exception class in multiple except blocks.

YieldInComprehensionViolation

WPS416 — Forbid yield keyword inside comprehensions.

NonUniqueItemsInHashViolation

WPS417 — Forbid duplicate items in hashes.

BaseExceptionSubclassViolation

WPS418 — Forbid exceptions inherited from BaseException.

TryExceptMultipleReturnPathViolation

WPS419 — Forbid multiple returning paths with try / except case.

WrongKeywordViolation

WPS420 — Forbid some python keywords.

WrongFunctionCallViolation

WPS421 — Forbid calling some built-in functions.

FutureImportViolation

WPS422 — Forbid __future__ imports.

RaiseNotImplementedViolation

WPS423 — Forbid NotImplemented exception.

BaseExceptionViolation

WPS424 — Forbid BaseException exception.

BooleanPositionalArgumentViolation

WPS425 — Forbid booleans as non-keyword parameters.

LambdaInsideLoopViolation

WPS426 — Forbid lambda inside loops.

UnreachableCodeViolation

WPS427 — Forbid unreachable code.

StatementHasNoEffectViolation

WPS428 — Forbid statements that do nothing.

MultipleAssignmentsViolation

WPS429 — Forbid multiple assignments on the same line.

NestedFunctionViolation

WPS430 — Forbid nested functions.

NestedClassViolation

WPS431 — Forbid nested classes.

MagicNumberViolation

WPS432 — Forbid magic numbers.

NestedImportViolation

WPS433 — Forbid imports nested in functions.

ReassigningVariableToItselfViolation

WPS434 — Forbid assigning a variable to itself.

ListMultiplyViolation

WPS435 — Forbid multiplying lists.

ProtectedModuleViolation

WPS436 — Forbid importing protected modules.

ProtectedAttributeViolation

WPS437 — Forbid protected attributes and methods.

StopIterationInsideGeneratorViolation

WPS438 — Forbid raising StopIteration inside generators.

WrongUnicodeEscapeViolation

WPS439 — Forbid Unicode escape sequences in binary strings.

BlockAndLocalOverlapViolation

WPS440 — Forbid overlapping local and block variables.

ControlVarUsedAfterBlockViolation

WPS441 — Forbid control variables after the block body.

OuterScopeShadowingViolation

WPS442 — Forbid shadowing variables from outer scopes.

UnhashableTypeInHashViolation

WPS443 — Forbid explicit unhashable types of asset items and dict keys.

WrongKeywordConditionViolation

WPS444 — Forbid explicit falsely-evaluated conditions with several keywords.

WrongNamedKeywordViolation

WPS445 — Forbid incorrectly named keywords in starred dicts.

ApproximateConstantViolation

WPS446 — Forbid approximate constants.

StringConstantRedefinedViolation

WPS447 — Forbid using the alphabet as a string.

IncorrectExceptOrderViolation

WPS448 — Forbid incorrect order of except.

FloatKeyViolation

WPS449 — Forbid float keys.

ProtectedModuleMemberViolation

WPS450 — Forbid importing protected objects from modules.

PositionalOnlyArgumentsViolation

WPS451 — Forbid positional only or / arguments.

LoopControlFinallyViolation

WPS452 — Forbid break and continue in a finally block.

ShebangViolation

WPS453 — Forbid executing a file with shebang incorrectly set.

BaseExceptionRaiseViolation

WPS454 — Forbid raising Exception or BaseException.

NonTrivialExceptViolation

WPS455 — Forbids using non-trivial expressions as a parameter for except.

FloatingNanViolation

WPS456 — Forbids using float("NaN") construct to generate NaN.

InfiniteWhileLoopViolation

WPS457 — Forbids use of infinite while True: loops.

ImportCollisionViolation

WPS458 — Forbids to import from already imported modules.

FloatComplexCompareViolation

WPS459 — Forbids comparisons with float and complex.

SingleElementDestructuringViolation

WPS460 — Forbids to have single element destructuring.

ForbiddenInlineIgnoreViolation

WPS461 — Forbids to use specific inline ignore violations.

WrongMultilineStringUseViolation

WPS462 — Frobids direct usage of multiline strings.

GetterWithoutReturnViolation

WPS463 — Forbids to have functions starting with get_ without returning a value.

EmptyCommentViolation

WPS464 — Forbid empty comments.

BitwiseAndBooleanMixupViolation

WPS465 — Forbid comparisons between bitwise and boolean expressions.

NewStyledDecoratorViolation

WPS466 — Forbid using complex grammar for using decorators.

BareRaiseViolation

WPS467 — Forbid using a bare raise keyword outside of except.

RedundantEnumerateViolation

WPS468 — Forbid using a placeholder (_) with enumerate.

RaiseFromItselfViolation

WPS469 — Forbid raising an exception from itself.

KwargsUnpackingInClassDefinitionViolation

WPS470 — Forbid kwarg unpacking in class definition.

ConsecutiveSlicesViolation

WPS471 — Forbid consecutive slices.

GettingElementByUnpackingViolation

WPS472 — Forbid getting first element using unpacking.

WrongEmptyLinesCountViolation

WPS473 — Limit empty lines in functions or methods body.

ImportObjectCollisionViolation

WPS474 — Do not allow importing the same object under different aliases.

Best practices

class WrongMagicCommentViolation(node=None, text=None, baseline=None)[source]

Bases: SimpleViolation

WPS400 — Restrict various control (such as magic) comments.

We do not allow:

  1. # noqa comment without specified violations

  2. # type: some_type comments to specify a type for typed_ast

This violation is reported at the top of the module, so it cannot be locally ignored.

Reasoning:

We cover several use-cases in a single rule. # noqa comment is restricted because it can hide other violations. # type: some_type comment is restricted because we can use type annotations instead.

Solution:

Use # noqa comments with specified error types. Use type annotations to specify types.

We still allow using # type: ignore comment, since sometimes it is required.

Example:

# Correct:
type = MyClass.get_type()  # noqa: WPS125
coordinate: int = 10
some.int_field = 'text'  # type: ignore

number: int
for number in some_untyped_iterable():
    ...

# Wrong:
type = MyClass.get_type()  # noqa
coordinate = 10  # type: int

Added in version 0.1.0.

code: ClassVar[int] = 400
error_template: ClassVar[str] = 'Found wrong magic comment: {0}'
full_code: ClassVar[str] = 'WPS400'
summary: ClassVar[str] = 'Restrict various control (such as magic) comments.'
class WrongDocCommentViolation(node, text=None, baseline=None)[source]

Bases: TokenizeViolation

WPS401 — Forbid empty doc comments (#:).

Reasoning:

Doc comments are used to provide documentation but supplying empty doc comments breaks this use-case. It is unclear why they can be used with no contents.

Solution:

Add some documentation to this comment or remove it.

Empty doc comments are not caught by the default pycodestyle checks.

Example:

# Correct:
#: List of allowed names:
NAMES_WHITELIST = ['feature', 'bug', 'research']

# Wrong:
#:
NAMES_WHITELIST = ['feature', 'bug', 'research']

Added in version 0.1.0.

code: ClassVar[int] = 401
error_template: ClassVar[str] = 'Found wrong doc comment'
full_code: ClassVar[str] = 'WPS401'
summary: ClassVar[str] = 'Forbid empty doc comments (``#:``).'
class OveruseOfNoqaCommentViolation(node=None, text=None, baseline=None)[source]

Bases: SimpleViolation

WPS402 — Forbid too many # noqa comments.

We count them on a per-module basis.

Reasoning:

Having too many # noqa comments makes your code less readable and indicates that there’s something wrong with it.

Solution:

Refactor your code to match our style. Or use a config file to switch off some checks.

Configuration:

This rule is configurable with --max-noqa-comments. Default: 10

Added in version 0.7.0.

error_template: ClassVar[str] = 'Found `noqa` comments overuse: {0}'
code: ClassVar[int] = 402
full_code: ClassVar[str] = 'WPS402'
summary: ClassVar[str] = 'Forbid too many ``# noqa`` comments.'
class OveruseOfNoCoverCommentViolation(node=None, text=None, baseline=None)[source]

Bases: SimpleViolation

WPS403 — Forbid too many # pragma: no cover comments.

We count them on a per-module basis. We use 5 as a default value.

Reasoning:

Having too many # pragma: no cover comments indicates that there’s something wrong with the code. Moreover, it makes your tests useless, since they do not cover a big portion of your code.

Solution:

Refactor your code to match the style. Or use a config file to switch off some checks.

Added in version 0.8.0.

error_template: ClassVar[str] = 'Found `no cover` comments overuse: {0}'
code: ClassVar[int] = 403
full_code: ClassVar[str] = 'WPS403'
summary: ClassVar[str] = 'Forbid too many ``# pragma: no cover`` comments.'
class ComplexDefaultValueViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS404 — Forbid complex defaults.

Anything that is not a ast.Name, ast.Attribute, ast.Str, ast.NameConstant, ast.Tuple, ast.Bytes, ast.Num or ast.Ellipsis should be moved out from defaults.

Reasoning:

It can be tricky. Nothing stops you from making database calls or HTTP requests in such expressions. It is also not readable for us.

Solution:

Move the expression out from default value.

Example:

# Correct:
SHOULD_USE_DOCTEST = 'PYFLAKES_DOCTEST' in os.environ
def __init__(self, with_doctest=SHOULD_USE_DOCTEST):

# Wrong:
def __init__(self, with_doctest='PYFLAKES_DOCTEST' in os.environ):

Added in version 0.8.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found complex default value'
code: ClassVar[int] = 404
previous_codes: ClassVar[Set[int]] = {459}
full_code: ClassVar[str] = 'WPS404'
summary: ClassVar[str] = 'Forbid complex defaults.'
class LoopVariableDefinitionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS405 — Forbid anything other than ast.Name to define loop variables.

Reasoning:

When defining a for loop with attributes, indexes, calls, or any other nodes it does dirty things inside.

Solution:

Use regular ast.Name variables. Or tuple of ast.Name variables. Star names are also fine.

Example:

# Correct:
for person in database.people():
    ...

# Wrong:
for context['person'] in database.people():
    ...

Added in version 0.8.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found wrong `for` loop variable definition'
code: ClassVar[int] = 405
previous_codes: ClassVar[Set[int]] = {460}
full_code: ClassVar[str] = 'WPS405'
summary: ClassVar[str] = 'Forbid anything other than ``ast.Name`` to define loop variables.'
class ContextManagerVariableDefinitionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS406 — Forbid anything other than ast.Name to define contexts.

Reasoning:

When defining a with context managers with attributes, indexes, calls, or any other nodes it does dirty things inside.

Solution:

Use regular ast.Name variables. Or tuple of ast.Name variables. Star names are also fine.

Example:

# Correct:
with open('README.md') as readme:
    ...

# Wrong:
with open('README.md') as files['readme']:
    ...

Added in version 0.8.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found wrong context manager variable definition'
code: ClassVar[int] = 406
previous_codes: ClassVar[Set[int]] = {461}
full_code: ClassVar[str] = 'WPS406'
summary: ClassVar[str] = 'Forbid anything other than ``ast.Name`` to define contexts.'
class MutableModuleConstantViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS407 — Forbid mutable constants on a module level.

Reasoning:

Constants should be immutable.

Solution:

Use immutable types for constants.

We only treat ast.Set, ast.Dict, ast.List and comprehensions as mutable things. All other nodes are still fine.

Example:

# Correct:
import types
CONST1 = frozenset((1, 2, 3))
CONST2 = (1, 2, 3)
CONST3 = types.MappingProxyType({'key': 'value'})

# Wrong:
CONST1 = {1, 2, 3}
CONST2 = [x for x in some()]
CONST3 = {'key': 'value'}

Added in version 0.10.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found mutable module constant'
code: ClassVar[int] = 407
previous_codes: ClassVar[Set[int]] = {466}
full_code: ClassVar[str] = 'WPS407'
summary: ClassVar[str] = 'Forbid mutable constants on a module level.'
class SameElementsInConditionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS408 — Forbid using the same logical conditions in one expression.

Reasoning:

Using the same name in a logical condition more than once indicates that you are either making a logical mistake, or just over-complicating your design.

Solution:

Remove the duplicated condition.

Example:

# Correct:
if some_value or other_value:
    ...

# Wrong:
if some_value or some_value:
    ...

Added in version 0.10.0.

Changed in version 0.11.0.

Changed in version 0.13.0.

error_template: ClassVar[str] = 'Found duplicate logical condition'
code: ClassVar[int] = 408
previous_codes: ClassVar[Set[int]] = {469}
full_code: ClassVar[str] = 'WPS408'
summary: ClassVar[str] = 'Forbid using the same logical conditions in one expression.'
class HeterogeneousCompareViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS409 — Forbid heterogeneous operators in one comparison.

Note, that we do allow mixing > with >= and < with <= operators.

Reasoning:

This is hard to read and understand.

Solution:

Refactor the expression to have separate parts joined with and boolean operator.

Example:

# Correct:
if x == y == z:
    ...

if x > y >= z:
    ...

# Wrong:
if x > y == 5:
    ...

if x == y != z:
    ...

Added in version 0.10.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found heterogeneous compare'
code: ClassVar[int] = 409
previous_codes: ClassVar[Set[int]] = {471}
full_code: ClassVar[str] = 'WPS409'
summary: ClassVar[str] = 'Forbid heterogeneous operators in one comparison.'
class WrongModuleMetadataViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS410 — Forbid some module-level variables.

Reasoning:

We discourage using module variables like __author__, because code should not contain any metadata.

Solution:

Place all the metadata in setup.py, setup.cfg, or pyproject.toml. Use proper docstrings and packaging classifiers. Use importlib.metadata if you need to import this data into your app.

See MODULE_METADATA_VARIABLES_BLACKLIST for full list of bad names.

Example:

# Wrong:
__author__ = 'Nikita Sobolev'
__version__ = 0.1.2

Added in version 0.1.0.

error_template: ClassVar[str] = 'Found wrong metadata variable: {0}'
code: ClassVar[int] = 410
full_code: ClassVar[str] = 'WPS410'
summary: ClassVar[str] = 'Forbid some module-level variables.'
class EmptyModuleViolation(node=None, text=None, baseline=None)[source]

Bases: SimpleViolation

WPS411 — Forbid empty modules.

Reasoning:

Why is it even there? Do not pollute your project with empty files.

Solution:

If you have an empty module there are two ways to handle that:

  1. delete it

  2. drop some documentation in it, so you will explain why it is there

Added in version 0.1.0.

error_template: ClassVar[str] = 'Found empty module'
code: ClassVar[int] = 411
full_code: ClassVar[str] = 'WPS411'
summary: ClassVar[str] = 'Forbid empty modules.'
class InitModuleHasLogicViolation(node=None, text=None, baseline=None)[source]

Bases: SimpleViolation

WPS412 — Forbid logic inside __init__ module.

Reasoning:

If you have logic inside the __init__ module It means several things:

  1. you are keeping some outdated stuff there, you need to refactor

  2. you are placing this logic in the wrong file, just create another one

  3. you are doing some dark magic, and you should not do that

Solution:

Put your code in other modules.

However, we allow some contents inside the __init__ module:

  1. comments, since they are dropped before AST comes in play

  2. docstrings are used sometimes when required to state something

It is also fine when you have different users that use your code. And you do not want to break everything for them. In this case, this rule can be configured.

Configuration:

This rule is configurable with --i-control-code and --i-dont-control-code. Default: True

When using --i-dont-control-code it is still recommended to only have imports in your __init__.py.

Added in version 0.1.0.

error_template: ClassVar[str] = 'Found `__init__.py` module with logic'
code: ClassVar[int] = 412
full_code: ClassVar[str] = 'WPS412'
summary: ClassVar[str] = 'Forbid logic inside ``__init__`` module.'
class BadMagicModuleFunctionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS413 — Forbid __getattr__ and __dir__ module magic methods.

Reasoning:

It does not bring any features, only making it harder to understand what is going on.

Solution:

Refactor your code to use custom methods instead.

Configuration:

This rule is configurable with --i-control-code and --i-dont-control-code.

Default:

True

Added in version 0.9.0.

error_template: ClassVar[str] = 'Found bad magic module function: {0}'
code: ClassVar[int] = 413
full_code: ClassVar[str] = 'WPS413'
summary: ClassVar[str] = 'Forbid ``__getattr__`` and ``__dir__`` module magic methods.'
class WrongUnpackingViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS414 — Forbid tuple unpacking with side-effects.

Reasoning:

Having unpacking with side-effects is very dirty. You might get in serious and very hard-to-debug troubles because of this technique so do not use it.

This includes assigning to attributes, as this results in modifying the instance. Every modification should be explicit on it’s own line.

Solution:

Use unpacking only with variables, not any other entities.

Example:

# Correct:
reader, writer = call()
self.reader = reader
self.writer = writer

# Wrong:
first, some_dict['alias'] = some()
self.reader, self.writer = call()

Added in version 0.6.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found incorrect unpacking target'
code: ClassVar[int] = 414
previous_codes: ClassVar[Set[int]] = {446}
full_code: ClassVar[str] = 'WPS414'
summary: ClassVar[str] = 'Forbid tuple unpacking with side-effects.'
class DuplicateExceptionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS415 — Forbid the same exception class in multiple except blocks.

Reasoning:

Having the same exception name in different blocks means that something is not right: since only one branch will work. Another one will always be ignored. So, that is an error.

Solution:

Use unique exception handling rules.

Example:

# Correct:
try:
    ...
except ValueError:
    ...

# Wrong:
try:
    ...
except ValueError:
    ...
except ValueError:
    ...

Added in version 0.6.0.

Changed in version 0.11.0.

Changed in version 0.19.0: Supports try/except* as well.

error_template: ClassVar[str] = 'Found duplicate exception: {0}'
code: ClassVar[int] = 415
previous_codes: ClassVar[Set[int]] = {447}
full_code: ClassVar[str] = 'WPS415'
summary: ClassVar[str] = 'Forbid the same exception class in multiple ``except`` blocks.'
class YieldInComprehensionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS416 — Forbid yield keyword inside comprehensions.

This is a SyntaxError starting from python3.8.

Reasoning:

Having the yield keyword inside comprehensions is error-prone. You can shoot yourself in the foot by an inaccurate usage of this feature.

Solution:

Use regular for loops with yield keywords or create a separate generator function.

Example:

# Wrong:
list((yield letter) for letter in 'ab')
# Will resilt in: ['a', None, 'b', None]

list([(yield letter) for letter in 'ab'])
# Will result in: ['a', 'b']

Added in version 0.7.0.

Changed in version 0.11.0.

Changed in version 0.18.0: No longer produced, kept here for historic reasons.

error_template: ClassVar[str] = 'Found `yield` inside comprehension'
code: ClassVar[int] = 416
previous_codes: ClassVar[Set[int]] = {448}
deprecated: ClassVar[bool] = True
full_code: ClassVar[str] = 'WPS416'
summary: ClassVar[str] = 'Forbid ``yield`` keyword inside comprehensions.'
class NonUniqueItemsInHashViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS417 — Forbid duplicate items in hashes.

Reasoning:

When you explicitly put duplicate items in set literals or in dict keys it just does not make any sense since hashes cannot contain duplicate items and they will be removed anyway.

Solution:

Remove duplicate items.

Example:

# Correct:
some_set = {'a', variable1}
some_set = {make_call(), make_call()}

# Wrong:
some_set = {'a', 'a', variable1, variable1}

Things that we consider duplicates: builtins and variables. These nodes are not checked because they may return different results:

  • function and method calls

  • comprehensions

  • attributes

  • subscribe operations

Added in version 0.7.0.

Changed in version 0.11.0.

Changed in version 0.12.0.

error_template: ClassVar[str] = 'Found non-unique item in hash: {0}'
code: ClassVar[int] = 417
previous_codes: ClassVar[Set[int]] = {449}
full_code: ClassVar[str] = 'WPS417'
summary: ClassVar[str] = 'Forbid duplicate items in hashes.'
class BaseExceptionSubclassViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS418 — Forbid exceptions inherited from BaseException.

Reasoning:

BaseException is a special case: it is not designed to be extended by users. A lot of your except Exception cases won’t work. That’s incorrect and dangerous.

Solution:

Change the base class to Exception.

Example:

# Correct:
class MyException(Exception):
    ...

# Wrong:
class MyException(BaseException):
    ...

Added in version 0.7.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found exception inherited from `BaseException`'
code: ClassVar[int] = 418
previous_codes: ClassVar[Set[int]] = {450}
full_code: ClassVar[str] = 'WPS418'
summary: ClassVar[str] = 'Forbid exceptions inherited from ``BaseException``.'
class TryExceptMultipleReturnPathViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS419 — Forbid multiple returning paths with try / except case.

Note, that we check for any return, break, or raise nodes.

Reasoning:

The problem with return in else and finally is that it is impossible to say what value is going to be returned without looking up the implementation details. Why? Because return does not expect that some other code will be executed after it. But, finally is always executed, even after return. And else will not be executed when there are no exceptions in try case and a return statement.

Solution:

Remove return from one of the cases.

Example:

# Correct:
try:
    return 1
except YourException:
    ...
finally:
    clear_things_up()

# Wrong:
try:
    return 1  # this line will never return
except Exception:
    ...
finally:
    return 2  # this line will actually return

try:
    return 1  # this line will actually return
except ZeroDivisionError:
    ...
else:
    return 0  # this line will never return

Added in version 0.7.0.

Changed in version 0.11.0.

Changed in version 0.12.0.

Changed in version 0.19.0: Supports try/except* as well.

error_template: ClassVar[str] = 'Found `try`/`else`/`finally` with multiple return paths'
code: ClassVar[int] = 419
previous_codes: ClassVar[Set[int]] = {458}
full_code: ClassVar[str] = 'WPS419'
summary: ClassVar[str] = 'Forbid multiple returning paths with ``try`` / ``except`` case.'
class WrongKeywordViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS420 — Forbid some python keywords.

Reasoning:

Using some keywords generally causes more pain than it relieves.

del keyword is not composable with other functions, you cannot pass it as a regular function. It is also quite error-prone due to __del__ magic method complexity and that del is actually used to nullify variables and delete them from the execution scope. Moreover, it has a lot of substitutions. You won’t miss it!

pass keyword is just useless by design. There’s no use-case for it. Because it does literally nothing.

global and nonlocal promote bad-practices of having an external mutable state somewhere. This solution does not scale and leads to multiple possible mistakes in the future.

Solution:

Solutions differ from keyword to keyword. pass should be replaced with docstring or contextlib.suppress. del should be replaced with specialized methods like .pop(). global and nonlocal usages should be refactored.

Added in version 0.1.0.

error_template: ClassVar[str] = 'Found wrong keyword: {0}'
code: ClassVar[int] = 420
full_code: ClassVar[str] = 'WPS420'
summary: ClassVar[str] = 'Forbid some ``python`` keywords.'
class WrongFunctionCallViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS421 — Forbid calling some built-in functions.

Reasoning:

Some functions are only suitable for very specific use cases, we forbid the use of them in a free manner.

See FUNCTIONS_BLACKLIST for the full list of blacklisted functions.

Added in version 0.1.0.

error_template: ClassVar[str] = 'Found wrong function call: {0}'
code: ClassVar[int] = 421
full_code: ClassVar[str] = 'WPS421'
summary: ClassVar[str] = 'Forbid calling some built-in functions.'
class FutureImportViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS422 — Forbid __future__ imports.

Reasoning:

Almost all __future__ imports are legacy python2 compatibility tools that are no longer required.

Solution:

Remove them. Drop python2 support.

Except, there are some new ones for python4 support. See FUTURE_IMPORTS_WHITELIST for the full list of allowed future imports.

Example:

# Correct:
from __future__ import annotations

# Wrong:
from __future__ import print_function

Added in version 0.1.0.

error_template: ClassVar[str] = 'Found future import: {0}'
code: ClassVar[int] = 422
full_code: ClassVar[str] = 'WPS422'
summary: ClassVar[str] = 'Forbid ``__future__`` imports.'
class RaiseNotImplementedViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS423 — Forbid NotImplemented exception.

Reasoning:

NotImplemented and NotImplementedError look similar but they have different use cases. Use cases of NotImplemented are too limited to be generally available.

Solution:

Use NotImplementedError.

Example:

# Correct:
raise NotImplementedError('To be done')

# Wrong:
raise NotImplemented

Added in version 0.1.0.

error_template: ClassVar[str] = 'Found raise NotImplemented'
code: ClassVar[int] = 423
full_code: ClassVar[str] = 'WPS423'
summary: ClassVar[str] = 'Forbid ``NotImplemented`` exception.'
class BaseExceptionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS424 — Forbid BaseException exception.

Reasoning:

We can silence system exit and keyboard interrupt with this exception handler. It is almost the same as raw except: block.

Solution:

Handle Exception, KeyboardInterrupt, GeneratorExit, and SystemExit separately. Do not use the plain except: keyword.

Example:

# Correct:
except Exception as ex: ...

# Wrong:
except BaseException as ex: ...

Added in version 0.3.0.

error_template: ClassVar[str] = 'Found except `BaseException`'
code: ClassVar[int] = 424
full_code: ClassVar[str] = 'WPS424'
summary: ClassVar[str] = 'Forbid ``BaseException`` exception.'
class BooleanPositionalArgumentViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS425 — Forbid booleans as non-keyword parameters.

Reasoning:

Passing booleans as regular positional parameters is very non-descriptive. It is almost impossible to tell what this parameter means and you almost always have to look up the implementation to tell what is going on. The only exception from this rule is passing a boolean as a non-keyword argument when it is the only passed argument.

Solution:

Pass booleans as keywords only. This will help you to save extra context on what’s going on.

Example:

# Correct:
UserRepository.update(True)
UsersRepository.add(user, cache=True)

# Wrong:
UsersRepository.add(user, True)

Added in version 0.6.0.

error_template: ClassVar[str] = 'Found boolean non-keyword argument: {0}'
code: ClassVar[int] = 425
full_code: ClassVar[str] = 'WPS425'
summary: ClassVar[str] = 'Forbid booleans as non-keyword parameters.'
class LambdaInsideLoopViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS426 — Forbid lambda inside loops.

We check while, for, and async for loop bodies. We also check comprehension value parts.

Reasoning:

It is error-prone to use lambda inside for and while loops due to the famous late-binding.

Solution:

Use regular functions, factory functions, or partial functions. Save yourself from possible confusion.

Example:

# Correct:
for index in range(10):
    some.append(partial_function(index))

# Wrong:
for index in range(10):
    some.append(lambda index=index: index * 10))
    other.append(lambda: index * 10))

Added in version 0.5.0.

Changed in version 0.11.0.

Changed in version 0.14.0.

error_template: ClassVar[str] = "Found `lambda` in loop's body"
code: ClassVar[int] = 426
previous_codes: ClassVar[Set[int]] = {442}
full_code: ClassVar[str] = 'WPS426'
summary: ClassVar[str] = 'Forbid ``lambda`` inside loops.'
class UnreachableCodeViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS427 — Forbid unreachable code.

What is unreachable code? It is some lines of code that cannot be executed by python’s interpreter.

This is probably caused by return or raise statements. However, we cannot cover 100% of truly unreachable code by this rule. This happens due to the dynamic nature of python. For example, detecting that 1 / some_value would sometimes raise an exception is too complicated and is out of the scope of this rule.

Reasoning:

Having dead code in your project is an indicator that you do not care about your codebase at all. It dramatically reduces code quality and readability. It also demotivates team members.

Solution:

Delete any unreachable code you have or refactor it, if this happens by your mistake.

Example:

# Correct:
def some_function():
    print('This line is reachable, all good')
    return 5

# Wrong:
def some_function():
    return 5
    print('This line is unreachable')

Added in version 0.5.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found unreachable code'
code: ClassVar[int] = 427
previous_codes: ClassVar[Set[int]] = {443}
full_code: ClassVar[str] = 'WPS427'
summary: ClassVar[str] = 'Forbid unreachable code.'
class StatementHasNoEffectViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS428 — Forbid statements that do nothing.

Reasoning:

Statements that just access the value or expressions used as statements indicate that your code contains deadlines. They just pollute your codebase and do nothing.

Solution:

Refactor your code in case it was a typo or error or just delete this code.

Example:

# Correct:
def some_function():
    price = 8 + 2
    return price

# Wrong:
def some_function():
    8 + 2
    print

Added in version 0.5.0.

Changed in version 0.11.0.

Changed in version 0.19.1: Do not report ... when used in a function or class body as a single node.

error_template: ClassVar[str] = 'Found statement that has no effect'
code: ClassVar[int] = 428
previous_codes: ClassVar[Set[int]] = {444}
full_code: ClassVar[str] = 'WPS428'
summary: ClassVar[str] = 'Forbid statements that do nothing.'
class MultipleAssignmentsViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS429 — Forbid multiple assignments on the same line.

Reasoning:

Multiple assignments on the same line might not do what you think they do. They can also grow pretty long and you might not notice the rising complexity of your code.

Solution:

Use separate lines for each assignment.

Example:

# Correct:
a = 1
b = 1

# Wrong:
a = b = 1

Added in version 0.6.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found multiple assign targets'
code: ClassVar[int] = 429
previous_codes: ClassVar[Set[int]] = {445}
full_code: ClassVar[str] = 'WPS429'
summary: ClassVar[str] = 'Forbid multiple assignments on the same line.'
class NestedFunctionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS430 — Forbid nested functions.

Reasoning:

Nesting functions is bad practice. It is hard to test them and it is hard to separate them later. People tend to overuse closures, so it’s hard to manage the dataflow.

Solution:

Just write flat functions, there’s no need to nest them. Pass parameters as normal arguments, do not use closures until you need them for decorators or factories.

We also forbid nesting lambda and async functions.

See NESTED_FUNCTIONS_WHITELIST for the whole list of whitelisted names.

Example:

# Correct:
def do_some(): ...
def other(): ...

# Wrong:
def do_some():
    def inner():
        ...

Added in version 0.1.0.

error_template: ClassVar[str] = 'Found nested function: {0}'
code: ClassVar[int] = 430
full_code: ClassVar[str] = 'WPS430'
summary: ClassVar[str] = 'Forbid nested functions.'
class NestedClassViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS431 — Forbid nested classes.

Reasoning:

Nested classes are really hard to manage. You cannot even create an instance of this class in many cases. Testing them is also really hard.

Solution:

Just write flat classes, there’s no need to nest them. If you are nesting classes inside a function for parametrization, then you will probably need to use a different design (or metaclasses).

Configuration:

This rule is configurable with --nested-classes-whitelist. Default: (‘Meta’, ‘Params’, ‘Config’)

Example:

# Correct:
class Some: ...
class Other: ...

# Wrong:
class Some:
    class Inner:
        ...

Added in version 0.1.0.

Changed in version 0.13.0.

error_template: ClassVar[str] = 'Found nested class: {0}'
code: ClassVar[int] = 431
full_code: ClassVar[str] = 'WPS431'
summary: ClassVar[str] = 'Forbid nested classes.'
class MagicNumberViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS432 — Forbid magic numbers.

What do we call a “magic number”? Well, it is actually any number that appears in your code out of nowhere. Like 42. Or 0.32.

Reasoning:

It is very hard to remember what these numbers mean. Why were they used? Should they ever be changed? Or are they eternal like 3.14?

Solution:

Give these numbers a name! Move them to a separate variable, giving more context to the reader. And by moving things into new variables you will trigger other complexity checks.

Example:

# Correct:
price_in_euro = 3.33  # could be changed later
total = get_items_from_cart() * price_in_euro

# Wrong:
total = get_items_from_cart() * 3.33

What are the numbers that we exclude from this check? Any numbers that are assigned to a variable, array, dictionary, or keyword arguments inside a function. int numbers that are in range [-10, 10] and some other common numbers, that are defined in MAGIC_NUMBERS_WHITELIST

Added in version 0.1.0.

code: ClassVar[int] = 432
error_template: ClassVar[str] = 'Found magic number: {0}'
full_code: ClassVar[str] = 'WPS432'
summary: ClassVar[str] = 'Forbid magic numbers.'
class NestedImportViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS433 — Forbid imports nested in functions.

Reasoning:

Usually, nested imports are used to fix the import cycle. So, nested imports show that there’s an issue with your design.

Solution:

You don’t need nested imports, you need to refactor your code. Introduce a new module or find another way to do what you want to do. Rethink how your layered architecture should look.

Example:

# Correct:
from my_module import some_function

def some(): ...

# Wrong:
def some():
    from my_module import some_function

Added in version 0.1.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found nested import'
code: ClassVar[int] = 433
previous_codes: ClassVar[Set[int]] = {435}
full_code: ClassVar[str] = 'WPS433'
summary: ClassVar[str] = 'Forbid imports nested in functions.'
class ReassigningVariableToItselfViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS434 — Forbid assigning a variable to itself.

Reasoning:

There is no need to do that. Generally, it is an indication of some errors or just dead code.

Example:

# Correct:
some = some + 1
x_coord, y_coord = y_coord, x_coord
flag = not flag

# Wrong:
some = some
x_coord, y_coord = x_coord, y_coord

Added in version 0.3.0.

Changed in version 0.16.0.

error_template: ClassVar[str] = 'Found reassigning variable to itself: {0}'
code: ClassVar[int] = 434
previous_codes: ClassVar[Set[int]] = {438}
full_code: ClassVar[str] = 'WPS434'
summary: ClassVar[str] = 'Forbid assigning a variable to itself.'
class ListMultiplyViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS435 — Forbid multiplying lists.

Reasoning:

When you multiply lists - it does not create new values, it creates references to the existing value. It is not what people mean in 99.9% of cases.

Solution:

Use list comprehension or loop instead.

Example:

# Wrong:
my_list = [1, 2, 3] * 3

Added in version 0.12.0.

error_template: ClassVar[str] = 'Found list multiply'
code: ClassVar[int] = 435
full_code: ClassVar[str] = 'WPS435'
summary: ClassVar[str] = 'Forbid multiplying lists.'
class ProtectedModuleViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS436 — Forbid importing protected modules.

Related to ProtectedModuleMemberViolation.

Reasoning:

When importing protected modules we break a contract that authors of this module enforce. This way we are not respecting encapsulation and it may break our code at any moment.

Solution:

Do not import protected modules. Respect the encapsulation.

Example:

# Correct:
import public_module
from some.public.module import FooClass

# Wrong:
import _compat
from some._protected.module import BarClass

Added in version 0.3.0.

Changed in version 0.11.0.

Changed in version 0.14.0.

error_template: ClassVar[str] = 'Found protected module import: {0}'
code: ClassVar[int] = 436
previous_codes: ClassVar[Set[int]] = {440}
full_code: ClassVar[str] = 'WPS436'
summary: ClassVar[str] = 'Forbid importing protected modules.'
class ProtectedAttributeViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS437 — Forbid protected attributes and methods.

Reasoning:

When using protected attributes and method we break a contract that authors of this class enforce. This way we are not respecting encapsulation and it may break our code at any moment.

Solution:

Do not use protected attributes and methods. Respect the encapsulation.

Example:

# Correct:
self._protected = 1
cls._hidden_method()
some.public()
super()._protected()

# Wrong:
print(some._protected)
instance._hidden()
self.container._internal = 10

Note, that it is possible to use protected attributes with self, cls, and super() as base names. We allow this so you can create and use protected attributes and methods inside the class context. This is how protected attributes should be used.

Added in version 0.3.0.

Changed in version 0.11.0.

error_template: ClassVar[str] = 'Found protected attribute usage: {0}'
code: ClassVar[int] = 437
previous_codes: ClassVar[Set[int]] = {441}
full_code: ClassVar[str] = 'WPS437'
summary: ClassVar[str] = 'Forbid protected attributes and methods.'
class StopIterationInsideGeneratorViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS438 — Forbid raising StopIteration inside generators.

Reasoning:

StopIteration should not be raised explicitly in generators.

Solution:

Use a return statement to get out of a generator.

Example:

# Correct:
def some_generator():
    if some_value:
        return
    yield 1

# Wrong:
def some_generator():
    if some_value:
        raise StopIteration
    yield 1

Added in version 0.12.0.

error_template: ClassVar[str] = 'Found `StopIteration` raising inside generator'
code: ClassVar[int] = 438
full_code: ClassVar[str] = 'WPS438'
summary: ClassVar[str] = 'Forbid raising ``StopIteration`` inside generators.'
class WrongUnicodeEscapeViolation(node, text=None, baseline=None)[source]

Bases: TokenizeViolation

WPS439 — Forbid Unicode escape sequences in binary strings.

Reasoning:

Binary strings do not work with Unicode. Having Unicode escape characters in there means that you have an error in your code.

Solution:

Use regular strings when escaping Unicode strings.

Example:

# Correct:
escaped = '\u0041'  # equals to 'A'

# Wrong:
escaped = b'\u0040'  # equals to b'\\u0040'

Is not reported for f-strings on python3.12+

Added in version 0.12.0.

error_template: ClassVar[str] = 'Found unicode escape in a binary string: {0}'
code: ClassVar[int] = 439
full_code: ClassVar[str] = 'WPS439'
summary: ClassVar[str] = 'Forbid Unicode escape sequences in binary strings.'
class BlockAndLocalOverlapViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS440 — Forbid overlapping local and block variables.

What we call local variables:

  1. Assigns and annotations

  2. Function arguments (they are local to the function body)

What we call block variables:

  1. Imports

  2. Functions and async functions definitions

  3. Classes, methods, and async methods definitions

  4. For and async for loops variables

  5. Except block exception aliases

We allow local variables to overlap themselves, we forbid block variables to overlap themselves.

Example:

# Correct:
my_value = 1
my_value = my_value + 1

# Wrong:
import my_value
my_value = 1  # overlaps with import

Added in version 0.12.0.

error_template: ClassVar[str] = 'Found block variables overlap: {0}'
code: ClassVar[int] = 440
full_code: ClassVar[str] = 'WPS440'
summary: ClassVar[str] = 'Forbid overlapping local and block variables.'
class ControlVarUsedAfterBlockViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS441 — Forbid control variables after the block body.

What we call block control variables:

  1. for loop unpacked variables

  2. with context variables

Reasoning:

Variables leaking from the blocks can damage your logic. It might not contain what you think they contain.

Solution:

Use names inside the scope they are defined. Create new functions to return values in case you need to use block variables: when searching for a value, etc.

Example:

# Correct:
for my_item in collection:
    print(my_item)

# Wrong:
for my_item in collection:
    ...
print(my_item)

Added in version 0.12.0.

Changed in version 0.14.0.

error_template: ClassVar[str] = 'Found control variable used after block: {0}'
code: ClassVar[int] = 441
full_code: ClassVar[str] = 'WPS441'
summary: ClassVar[str] = 'Forbid control variables after the block body.'
class OuterScopeShadowingViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS442 — Forbid shadowing variables from outer scopes.

We check the function, method, and module scopes. While we do not check the class scope. Because class level constants are not available via regular name, and they are scope to ClassName.var_name.

Reasoning:

Shadowing can lead you to a big pile of storage and unexpected bugs.

Solution:

Use different names.

Example:

# Correct:
def test(): ...

def other():
    test1 = 1

# Wrong:
def test(): ...

def other():
    test = 1  # shadows `test()` function

Added in version 0.12.0.

error_template: ClassVar[str] = 'Found outer scope names shadowing: {0}'
code: ClassVar[int] = 442
full_code: ClassVar[str] = 'WPS442'
summary: ClassVar[str] = 'Forbid shadowing variables from outer scopes.'
class UnhashableTypeInHashViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS443 — Forbid explicit unhashable types of asset items and dict keys.

Reasoning:

This will resolve in TypeError in runtime.

Solution:

Use hashable types to define set items and dict keys.

Example:

# Correct:
my_dict = {1: {}, (1, 2): [], (2, 3): {1, 2}}

# Wrong:
my_dict = {[1, 2]: [], {2, 3}: {1, 2}}

Added in version 0.12.0.

error_template: ClassVar[str] = 'Found unhashable item'
code: ClassVar[int] = 443
full_code: ClassVar[str] = 'WPS443'
summary: ClassVar[str] = 'Forbid explicit unhashable types of asset items and dict keys.'
class WrongKeywordConditionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS444 — Forbid explicit falsely-evaluated conditions with several keywords.

We check:

  • ast.While

  • ast.Assert

We do not check variables, attributes, calls, bool and bin operators, etc. We forbid constants and some expressions.

Reasoning:

Some conditions tell us that this node won’t work correctly. So, we need to check if we can fix that.

Solution:

Remove the unreachable node, or change the condition item.

Example:

# Correct:
assert some_variable

while True:
    ...

# Wrong:
assert []

while False:
    ...

Added in version 0.12.0.

Changed in version 0.13.0.

error_template: ClassVar[str] = 'Found incorrect keyword condition'
code: ClassVar[int] = 444
full_code: ClassVar[str] = 'WPS444'
summary: ClassVar[str] = 'Forbid explicit falsely-evaluated conditions with several keywords.'
class WrongNamedKeywordViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS445 — Forbid incorrectly named keywords in starred dicts.

Reasoning:

Using the incorrect keywords in a starred dict. Eg.: print(**{'@': 1}).

Solution:

Don’t use incorrect identifiers as keywords.

Example:

# Correct:
print(**{'end': '|'})

# Wrong:
print(**{'3end': '|'})

Added in version 0.13.0.

code: ClassVar[int] = 445
error_template: ClassVar[str] = 'Found incorrectly named keyword in the starred dict'
full_code: ClassVar[str] = 'WPS445'
summary: ClassVar[str] = 'Forbid incorrectly named keywords in starred dicts.'
class ApproximateConstantViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS446 — Forbid approximate constants.

Reasoning:

Some constants are already defined. No need to write them again, use existing values. We just compare numbers as strings and raise this violation when they start with the same chars.

Solution:

Use pre-defined constants.

Example:

# Correct:
from math import pi
random_number = 3.15
too_short = 3.1

# Wrong:
pi = 3.14

See MATH_APPROXIMATE_CONSTANTS for full list of math constants that we check for.

Added in version 0.13.0.

code: ClassVar[int] = 446
error_template: ClassVar[str] = 'Found approximate constant: {0}'
full_code: ClassVar[str] = 'WPS446'
summary: ClassVar[str] = 'Forbid approximate constants.'
class StringConstantRedefinedViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS447 — Forbid using the alphabet as a string.

Reasoning:

Some constants are already defined. No need to write to them again, use existing values. We just compare strings and raise this violation when they have the same chars.

Solution:

Use pre-defined constants.

Example:

# Correct:
import string
UPPERCASE_ALPH = string.ascii_uppercase
LOWERCASE_ALPH = string.ascii_lowercase

# Wrong:
GUESS_MY_NAME = "abcde...WXYZ"
UPPERCASE_ALPH = "ABCD...WXYZ"
LOWERCASE_ALPH = "abcd...wxyz"

Added in version 0.13.0.

error_template: ClassVar[str] = 'Found alphabet as strings: {0}'
code: ClassVar[int] = 447
full_code: ClassVar[str] = 'WPS447'
summary: ClassVar[str] = 'Forbid using the alphabet as a string.'
class IncorrectExceptOrderViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS448 — Forbid incorrect order of except.

Note, we only check for built-in exceptions because we cannot statically identify the inheritance order of custom ones.

Reasoning:

Using incorrect order of exceptions is error-prone, since you end up with some unreachable exception clauses.

Solution:

Use the correct order of exceptions.

Example:

# Correct:
try:
    ...
except ValueError:
    ...
except Exception:
    ...

# Wrong:
try:
    ...
except Exception:
    ...
except ValueError:
    ...

Added in version 0.13.0.

Changed in version 0.19.0: Supports try/except* as well.

error_template: ClassVar[str] = 'Found incorrect exception order'
code: ClassVar[int] = 448
full_code: ClassVar[str] = 'WPS448'
summary: ClassVar[str] = 'Forbid incorrect order of ``except``.'
class FloatKeyViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS449 — Forbid float keys.

Reasoning:

float is a very ugly data type. It has a lot of “precision” errors. When we use float as keys we can hit this wall. Moreover, we cannot use float keys with lists, by design.

Solution:

Use other data types: integers, decimals, or use fuzzy logic.

Example:

# Correct:
some = {1: 'a'}
some[1]

# Wrong:
some = {1.0: 'a'}
some[1.0]

Added in version 0.13.0.

error_template: ClassVar[str] = 'Found float used as a key'
code: ClassVar[int] = 449
full_code: ClassVar[str] = 'WPS449'
summary: ClassVar[str] = 'Forbid ``float`` keys.'
class ProtectedModuleMemberViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS450 — Forbid importing protected objects from modules.

Related to ProtectedModuleViolation.

Reasoning:

When importing a protected modules’ members, we break the contract which the authors of this module enforce. By disrespecting encapsulation, we may break the code at any moment.

Solution:

Do not import protected objects from modules. Respect the encapsulation.

Example:

# Correct:
from some.public.module import FooClass

# Wrong:
from some.module import _protected
from some.module import _protected as not_protected

Added in version 0.14.0.

error_template: ClassVar[str] = 'Found protected object import: {0}'
code: ClassVar[int] = 450
full_code: ClassVar[str] = 'WPS450'
summary: ClassVar[str] = 'Forbid importing protected objects from modules.'
class PositionalOnlyArgumentsViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS451 — Forbid positional only or / arguments.

Reasoning:

This is a very rare case. Almost exclusively used by C code and stdlib. There’s no point in declaring your own parameters as positional only. It will break your code!

Solution:

Use regular arguments. In case you are working with C, then this violation can be ignored.

Example:

# Correct:
def my_function(first, second):
    ...

# Wrong:
def my_function(first, /, second):
    ...

Added in version 0.14.0.

Changed in version 0.19.0: This check is now disabled, since / parameters are now useful. No longer produced, kept here for historic reasons.

error_template: ClassVar[str] = 'Found positional-only argument'
code: ClassVar[int] = 451
deprecated: ClassVar[bool] = True
full_code: ClassVar[str] = 'WPS451'
summary: ClassVar[str] = 'Forbid positional only or ``/`` arguments.'
class LoopControlFinallyViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS452 — Forbid break and continue in a finally block.

Related to TryExceptMultipleReturnPathViolation.

Reasoning:

Putting any control statements in finally is a terrible practice, because finally is implicitly called and can cause damage to your logic with its implicitness.

Solution:

Remove break and continue from finally blocks.

Example:

# Correct:
try:
    ...
finally:
    ...

# Wrong:
try:
    ...
finally:
    break

try:
    ...
finally:
    continue

Added in version 0.14.0.

error_template: ClassVar[str] = 'Found `break` or `continue` in `finally` block'
code: ClassVar[int] = 452
full_code: ClassVar[str] = 'WPS452'
summary: ClassVar[str] = 'Forbid ``break`` and ``continue`` in a ``finally`` block.'
class ShebangViolation(node=None, text=None, baseline=None)[source]

Bases: SimpleViolation

WPS453 — Forbid executing a file with shebang incorrectly set.

A violation is raised in these cases :
  • Shebang is present but the file is not executable.

  • The file is executable but no shebang is present.

  • Shebang is present but does not contain “python”.

  • Whitespace is present before the shebang.

  • Presence of blank lines or commented lines before the shebang.

Reasoning:

Setting the shebang incorrectly causes an executable mismatch.

Solution:

Ensure that the shebang is present on the first line, and contains “python”, and there is no leading whitespace.

Example:

# Correct:
#!/usr/bin/env python

# Wrong:
#!/usr/bin/env
    #!/usr/bin/env python

Added in version 0.14.0.

error_template: ClassVar[str] = 'Found executable mismatch: {0}'
code: ClassVar[int] = 453
full_code: ClassVar[str] = 'WPS453'
summary: ClassVar[str] = 'Forbid executing a file with shebang incorrectly set.'
class BaseExceptionRaiseViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS454 — Forbid raising Exception or BaseException.

Reasoning:

Exception and BaseException are inconvenient to catch. And when you catch them you can accidentally suppress other exceptions.

Solution:

Use a user-defined exception, subclassed from Exception.

Example:

# Correct:
raise UserNotFoundError
raise UserNotFoundError("cannot find user with the given id")

# Wrong:
raise Exception
raise Exception("user not found")
raise BaseException
raise BaseException("user not found")

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found wrong `raise` exception type: {0}'
code: ClassVar[int] = 454
full_code: ClassVar[str] = 'WPS454'
summary: ClassVar[str] = 'Forbid raising ``Exception`` or ``BaseException``.'
class NonTrivialExceptViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS455 — Forbids using non-trivial expressions as a parameter for except.

Reasoning:

Expressions used as an argument for except could be hard to read and hide real list of exceptions being expected to occur in the outlined code block.

Solution:

Use separate except blocks for each exception or provide a tuple of exception classes.

Example:

# Correct:
try:
    ...
except ValueError:
    ...
except TypeError:
    ...

try:
    ...
except (TypeError, ValueError):
    ...

# Wrong:
try:
    ...
except TypeError or ValueError:
    ...

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found non-trivial expression as an argument for "except"'
code: ClassVar[int] = 455
full_code: ClassVar[str] = 'WPS455'
summary: ClassVar[str] = 'Forbids using non-trivial expressions as a parameter for ``except``.'
class FloatingNanViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS456 — Forbids using float("NaN") construct to generate NaN.

Reasoning:

This method to generate NaN is really confusing and is a good way to catch a lot of unexpected bugs.

Solution:

Even if you’re 100% sure what you’re doing, use math.nan instead.

Example:

# Correct:
min(math.nan, 3)

# Wrong:
min(float("NAN"), 3)

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found "NaN" as argument to float()'
code: ClassVar[int] = 456
full_code: ClassVar[str] = 'WPS456'
summary: ClassVar[str] = 'Forbids using ``float("NaN")`` construct to generate NaN.'
class InfiniteWhileLoopViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS457 — Forbids use of infinite while True: loops.

Reasoning:

Infinite loops will cause bugs in code.

Solution:

Add either a return, raise, or break to handle the infinite loop.

Example:

# Correct:
while True:
    print('forever')
    break

# Wrong:
while True:
    print('forever')

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found an infinite while loop'
code: ClassVar[int] = 457
full_code: ClassVar[str] = 'WPS457'
summary: ClassVar[str] = 'Forbids use of infinite ``while True:`` loops.'
class ImportCollisionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS458 — Forbids to import from already imported modules.

Reasoning:

Importing objects from already imported modules is inconsistent and error-prone.

Solution:

Do not import objects from already imported modules or use aliases when it cannot be avoided.

Example:

# Correct:
import public
from public.module import FooClass

import hypothesis
from hypothesis import strategies as st

# Wrong:
from public import utils
from public.utils import something

import hypothesis
from hypothesis import strategies

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found imports collision: {0}'
code: ClassVar[int] = 458
full_code: ClassVar[str] = 'WPS458'
summary: ClassVar[str] = 'Forbids to import from already imported modules.'
class FloatComplexCompareViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS459 — Forbids comparisons with float and complex.

Reasoning:

This is a best practice rule, as float and complex suffer from representation error, leading to possibly incorrect results during comparison.

Solution:

Use fuzzy operators. 1. abs(f1 - f2) <= allowed_error 2. math.isclose(f1, f2) (for float) 3. cmath.isclose(c1, c2) (for complex) 4. Custom logic, not using operators

Example:

# Correct:
math.isclose(3.0, 0.3 / 0.1)
cmath.isclose(3 + 4j, (0.3 + 0.4j) / 0.1)

# Wrong:
3.0 == 0.3 / 0.1
3 + 4j == (0.3 + 0.4j) / 0.1

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found comparison with float or complex number'
code: ClassVar[int] = 459
full_code: ClassVar[str] = 'WPS459'
summary: ClassVar[str] = 'Forbids comparisons with ``float`` and ``complex``.'
class SingleElementDestructuringViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS460 — Forbids to have single element destructuring.

Reasoning:

Having single element destructuring is not readable.

Solution:

Use access by index instead.

Example:

# Correct:
first = single_element_list[0]

# Wrong:
(first,) = [1]

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found single element destructuring'
code: ClassVar[int] = 460
full_code: ClassVar[str] = 'WPS460'
summary: ClassVar[str] = 'Forbids to have single element destructuring.'
class ForbiddenInlineIgnoreViolation(node=None, text=None, baseline=None)[source]

Bases: SimpleViolation

WPS461 — Forbids to use specific inline ignore violations.

There can be forbidden a specific violation or whole class of violations.

Reasoning:

There are violations important for specific project that must not be ignored, e.g. complexity or best practices violations.

Solution:

Remove inline ignore for forbidden violations.

Configuration:

This rule is configurable with –forbidden-inline-ignore`. Default: ()

Added in version 0.15.0.

error_template: ClassVar[str] = 'Forbidden inline ignore: {0}'
code: ClassVar[int] = 461
full_code: ClassVar[str] = 'WPS461'
summary: ClassVar[str] = 'Forbids to use specific inline ignore violations.'
class WrongMultilineStringUseViolation(node, text=None, baseline=None)[source]

Bases: TokenizeViolation

WPS462 — Frobids direct usage of multiline strings.

Multiline strings are only allowed in docstrings or assignments to variables.

Reasoning:

Direct usage of multiline strings is not readable. One should not depend on the current indentation, e.g. in comparisons or function calls.

Solution:

Assign a multiline string to a variable.

Example:

# Correct:
multiline = """
    abc
    abc
"""

# Wrong:
function("""
    abc
    abc
""")

Added in version 0.15.0.

error_template: ClassVar[str] = 'Wrong multiline string usage'
code: ClassVar[int] = 462
full_code: ClassVar[str] = 'WPS462'
summary: ClassVar[str] = 'Frobids direct usage of multiline strings.'
class GetterWithoutReturnViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS463 — Forbids to have functions starting with get_ without returning a value.

Applies to both methods and functions.

Reasoning:

A get_ function is generally expected to return a value. Otherwise, it is most likely either an error or bad naming.

Solution:

Make sure getter functions return or yield a value on all execution paths, or rename the function.

Example:

# Correct:
def get_random_number():
     return random.randint(1, 10)

# Wrong:
def get_random_number():
     print('I do not return a value!')

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found a getter without a return value'
code: ClassVar[int] = 463
full_code: ClassVar[str] = 'WPS463'
summary: ClassVar[str] = 'Forbids to have functions starting with ``get_`` without returning a value.'
class EmptyCommentViolation(node, text=None, baseline=None)[source]

Bases: TokenizeViolation

WPS464 — Forbid empty comments.

Empty comments are only allowed in between valid comments.

Reasoning:

Empty comments that do not help formatting should be excluded.

Solution:

Remove the empty comments.

Example:

# Correct:

# First line
#
# Samples:
# One
# Two
my_var = 1

# Wrong:

#
my_var = 1

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found empty comment'
code: ClassVar[int] = 464
full_code: ClassVar[str] = 'WPS464'
summary: ClassVar[str] = 'Forbid empty comments.'
class BitwiseAndBooleanMixupViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS465 — Forbid comparisons between bitwise and boolean expressions.

Empty comments are only allowed in between valid comments.

Reasoning:

This case indicates that a person confused & with and and | with or. This can be the case if a person is coming from another language.

Solution:

Change bitwise operator to boolean operators.

Example:

# Correct:
first | 10

# Wrong:
result = ((first > 0) & False)

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found likely bitwise and boolean operation mixup'
code: ClassVar[int] = 465
full_code: ClassVar[str] = 'WPS465'
summary: ClassVar[str] = 'Forbid comparisons between bitwise and boolean expressions.'
class NewStyledDecoratorViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS466 — Forbid using complex grammar for using decorators.

This violation is only raised for python3.9+, earlier versions do not have this concept.

Reasoning:

New grammar allows to use decorators in a more liberal way. It is probably not a good idea. Because decorators should be simple and easy to read.

Solution:

Use names, attributes, and calls as decorators only. You are free to pass any args to function calls, however.

Example:

# Correct:
@some.decorator(args)
def my_function(): ...

# Wrong:
@some.decorator['method'] + other
def my_function(): ...

Added in version 0.15.0.

error_template: ClassVar[str] = 'Found new-styled decorator'
code: ClassVar[int] = 466
full_code: ClassVar[str] = 'WPS466'
summary: ClassVar[str] = 'Forbid using complex grammar for using decorators.'
class BareRaiseViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS467 — Forbid using a bare raise keyword outside of except.

Reasoning:

Using a bare raise outside of an except block causes a runtime error.

Solution:

Only use bare raise within an except block.

Example:

# Correct:
def smth():
    try:
        ...
    except:
        raise

# Wrong:
def smth():
    raise

Added in version 0.16.0.

error_template: ClassVar[str] = 'Found bare raise keyword'
code: ClassVar[int] = 467
full_code: ClassVar[str] = 'WPS467'
summary: ClassVar[str] = 'Forbid using a bare ``raise`` keyword outside of ``except``.'
class RedundantEnumerateViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS468 — Forbid using a placeholder (_) with enumerate.

Reasoning:

This adds no value and introduces additional complexity.

Solution:

Only use enumerate when you are going to do something with the index it returns.

Example:

# Correct:
for item in items:
  ...

# Wrong:
for _, item in enumerate(items):
  ...

Added in version 0.16.0.

error_template: ClassVar[str] = 'Found redundant use of `enumerate`'
code: ClassVar[int] = 468
full_code: ClassVar[str] = 'WPS468'
summary: ClassVar[str] = 'Forbid using a placeholder (``_``) with ``enumerate``.'
class RaiseFromItselfViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS469 — Forbid raising an exception from itself.

Reasoning:

It doesn’t make sense to raise an exception from it self, since the final behavior will be the same.

Solution:

Don’t raise an exception from itself.

Example:

# Correct:
ex = Exception('Some Exception')
raise ex

# Wrong:
ex = Exception('Some Exception')
raise ex from ex

Added in version 0.16.0.

error_template: ClassVar[str] = 'Found error raising from itself'
code: ClassVar[int] = 469
full_code: ClassVar[str] = 'WPS469'
summary: ClassVar[str] = 'Forbid raising an exception from itself.'
class KwargsUnpackingInClassDefinitionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS470 — Forbid kwarg unpacking in class definition.

Reasoning:

Dynamic class generation with unknown arguments is bad because it creates too much flexibility and possibilities for errors. It also limits the typechecking capabilities.

Solution:

Use keyword arguments normally without unpacking them.

Example:

# Correct:
class MyClass(argument='argument'):
    ...

# Wrong:
arguments = {'argument': 'argument'}
class MyClass(**arguments):
    ...

Added in version 0.16.0.

error_template: ClassVar[str] = 'Found kwarg unpacking in class definition'
code: ClassVar[int] = 470
full_code: ClassVar[str] = 'WPS470'
summary: ClassVar[str] = 'Forbid kwarg unpacking in class definition.'
class ConsecutiveSlicesViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS471 — Forbid consecutive slices.

Reasoning:

Consecutive slices reduce readability of the code and obscure intended meaning of the expression.

Solution:

Compress multiple consecutive slices into a single one.

Example:

# Correct:
my_list[1:3]

# Wrong:
my_list[1:][:2]

Added in version 0.16.0.

error_template: ClassVar[str] = 'Found consecutive slices'
code: ClassVar[int] = 471
full_code: ClassVar[str] = 'WPS471'
summary: ClassVar[str] = 'Forbid consecutive slices.'
class GettingElementByUnpackingViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS472 — Forbid getting first element using unpacking.

Reasoning:

Performance. Prefixing unused variables with underscore is nothing more than convention, Python still creates these variables. So, unpacking above makes a new unused list which is slow.

Solution:

Use collection[0] or next(iter(collection))

Example:

# Correct:
first = some_collection[0]
first = next(iter(collection))

# Wrong:
first, *_rest = some_collection

Added in version 0.16.0.

error_template: ClassVar[str] = 'Found unpacking used to get a single element from a collection'
code: ClassVar[int] = 472
full_code: ClassVar[str] = 'WPS472'
summary: ClassVar[str] = 'Forbid getting first element using unpacking.'
class WrongEmptyLinesCountViolation(node, text=None, baseline=None)[source]

Bases: TokenizeViolation

WPS473 — Limit empty lines in functions or methods body.

Reasoning:

It’s not holistic to have functions or methods that contain many empty lines, and it makes sense to divide the method into several ones.

Solution:

Limit count of empty lines of the function or method body By default, we allow 1 empty line for 2 non-empty lines.

Example:

# Correct:
def func(name):
    foo()
    if name == 'Moonflower':
        print('Love')
    baz()

# Wrong:
def func(name):
    foo()

    if name == 'Moonflower':
        print('Love')

    baz()
Configuration:

This rule is configurable with --exps-for-one-empty-line. Default: 2

Added in version 0.17.0.

error_template: ClassVar[str] = 'Found too many empty lines in `def`: {0}'
code: ClassVar[int] = 473
full_code: ClassVar[str] = 'WPS473'
summary: ClassVar[str] = 'Limit empty lines in functions or methods body.'
class ImportObjectCollisionViolation(node, text=None, baseline=None)[source]

Bases: ASTViolation

WPS474 — Do not allow importing the same object under different aliases.

Reasoning:

This can lead to reader confusion, because two names usually mean two different things.

Solution:

Remove useless aliases.

Example:

# Correct:
from module import name

# Wrong:
from module import name, name as alias

Added in version 0.19.0.

error_template: ClassVar[str] = 'Found import object collision: {0}'
code: ClassVar[int] = 474
full_code: ClassVar[str] = 'WPS474'
summary: ClassVar[str] = 'Do not allow importing the same object under different aliases.'