Complexity¶
These checks find flaws in your application design.
We try to stick to “the magical 7 ± 2 number” when counting things. https://en.wikipedia.org/wiki/The_Magical_Number_Seven,_Plus_or_Minus_Two
That’s how many objects we can keep in our memory at a time. We try hard not to exceed the memory capacity limit.
You can also find interesting reading about “Cognitive complexity”: https://www.sonarsource.com/docs/CognitiveComplexity.pdf
Note
Simple is better than complex. Complex is better than complicated.
Summary¶
Forbids to have modules with complex lines. |
|
Forbids to have modules with too many imports. |
|
Forbids to have many classes and functions in a single module. |
|
Forbids to have modules with too many imported names. |
|
Forbids to have overused expressions in a module, function or method. |
|
Forbids to have too many local variables in the unit of code. |
|
Forbids to have too many arguments for a function or method. |
|
Forbids placing too many |
|
Forbids putting too many expressions in a single function. |
|
Forbids to have many methods in a single class. |
|
Restrict the maximum number of base classes. |
|
Restrict the maximum number of decorators. |
|
Forbids placing too many |
|
Forbids placing too many |
|
Forbids to have consecutive expressions with too deep access level. |
|
Forbids nesting blocks too deep. |
|
Forbids to have complex lines. |
|
Forbids to have conditions with too many logical operators. |
|
Forbids to use many |
|
Forbids to have too many |
|
Forbids to have too many |
|
Forbids to over-use string constants. |
|
Forbids to yield too long tuples. |
|
Forbids to have too long compare expressions. |
|
Forbids to have |
|
Forbids to have |
|
Forbids to have functions with too high cognitive complexity. |
|
Forbids to have modules with too high average cognitive complexity. |
|
Forbids too long call chains. |
|
Forbids too complex annotations. |
|
Forbids |
Module complexity¶
-
class
JonesScoreViolation
(node=None, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.SimpleViolation
Forbids to have modules with complex lines.
We are using Jones Complexity algorithm to count module’s score. See
LineComplexityViolation
for details of per-line-complexity. How it is done: we count complexity per line, then measuring the median complexity across the lines in the whole module.- Reasoning:
Having complex modules will decrease your code maintainability.
- Solution:
Refactor the module contents.
- Configuration:
This rule is configurable with
--max-jones-score
. Default: 12
New in version 0.1.0.
-
error_template
= 'Found module with high Jones Complexity score: {0}'¶
-
code
= 200¶
-
class
TooManyImportsViolation
(node=None, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.SimpleViolation
Forbids to have modules with too many imports.
Namespaces are one honking great idea – let’s do more of those!
- Reasoning:
Having too many imports without prefixes is quite expensive. You have to memorize all the source locations of the imports. And sometimes it is hard to remember what kind of functions and classes are already injected into your context.
It is also a questionable design if a single module has a lot of imports. Why a single module has so many dependencies? So, it becomes too coupled.
- Solution:
Refactor the imports to import a common namespace. Something like
from package import module
and then use it likemodule.function()
.Or refactor your code and split the complex module into several ones.
We do not make any differences between
import
andfrom ... import ...
.- Configuration:
This rule is configurable with
--max-imports
. Default: 12
New in version 0.1.0.
-
error_template
= 'Found module with too many imports: {0}'¶
-
code
= 201¶
-
class
TooManyModuleMembersViolation
(node=None, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.SimpleViolation
Forbids to have many classes and functions in a single module.
- Reasoning:
Having many classes and functions in a single module is a bad thing. Soon it will be hard to read through this code and understand it.
- Solution:
It is better to split this module into several modules or a package.
We do not make any differences between classes and functions in this check. They are treated as the same unit of logic. We also do not care about functions and classes being public or not. However, methods are counted separately on a per-class basis.
- Configuration:
This rule is configurable with
--max-module-members
. Default: 7
New in version 0.1.0.
-
error_template
= 'Found too many module members: {0}'¶
-
code
= 202¶
-
class
TooManyImportedNamesViolation
(node=None, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.SimpleViolation
Forbids to have modules with too many imported names.
Namespaces are one honking great idea – let’s do more of those!
- Reasoning:
Having too many imported names without prefixes is quite expensive. You have to memorize all the source locations of the imports. And sometimes it is hard to remember what kind of functions and classes are already injected into your context.
It is also a questionable design if a single module has a lot of imports. Why a single module has so many dependencies? So, it becomes too coupled.
- Solution:
Refactor the imports to import a common namespace. Something like
from package import module
and then use it likemodule.function()
.Or refactor your code and split the complex module into several ones.
Example:
# Correct: import module # 1 imported name # Wrong: from module import func1, func2, ..., funcN # N imported names
We do not make any differences between
import
andfrom ... import ...
.- Configuration:
This rule is configurable with
--max-imported-names
. Default: 50
New in version 0.12.0.
-
error_template
= 'Found module with too many imported names: {0}'¶
-
code
= 203¶
-
class
OverusedExpressionViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have overused expressions in a module, function or method.
What do we call an “overused expression”? When you use any expression (like
user_dict['age']
for example) inside your code, you always have to track that you are not using it “too much”. Because if that expression is everywhere inside your code, it is a sign of a problem. It means that you are missing an abstraction.We check overused expression on two levels:
per each function
per all module
Related to
TooManyExpressionsViolation
.- Reasoning:
Overusing expression lead to losing the parts that can and should be refactored into variables, methods, and properties of objects.
- Solution:
Refactor expressions to be an attribute, a method, or a new variable.
- Configuration:
This rule is configurable with
--max-module-expressions
. Default: 7And with
--max-function-expressions
. Default: 4
New in version 0.12.0.
Changed in version 0.14.0.
-
error_template
= 'Found overused expression: {0}'¶
-
code
= 204¶
Structure complexity¶
-
class
TooManyLocalsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have too many local variables in the unit of code.
- Reasoning:
Having too many variables in a single function is a bad thing. Soon, you will find troubles to understand what this variable means. It will also become hard to name new variables.
- Solution:
If you have too many variables in a function, you have to refactor it.
What counts as a local variable? We only count variable as local in the following case: it is assigned inside the function body. We do not count variables defined inside comprehensions as local variables, since it is impossible to use them outside of the comprehension.
Example:
def first_function(param): first_var = 1 def second_function(argument): second_var = 1 argument = int(argument) third_var, _ = some_call()
In this example we will count as locals only several variables:
first_var
, because it is assigned inside the function’s bodysecond_var
, because it is assigned inside the function’s bodyargument
, because it is reassigned inside the function’s bodythird_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.- Configuration:
This rule is configurable with
--max-local-variables
. Default: 5
New in version 0.1.0.
-
error_template
= 'Found too many local variables: {0}'¶
-
code
= 210¶
-
class
TooManyArgumentsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have too many arguments for a function or method.
- Reasoning:
This is an indicator of a bad design. When a function requires many arguments it shows that it is required to refactor this piece of code. It also indicates that function does too many things at once.
- Solution:
Split function into several functions. Then it will be easier to use them.
- Configuration:
This rule is configurable with
--max-arguments
. Default: 5
New in version 0.1.0.
-
error_template
= 'Found too many arguments: {0}'¶
-
code
= 211¶
-
class
TooManyReturnsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids placing too many
return
statements into the function.- Reasoning:
When there are too many
return
keywords, functions are hard to test. They are also hard to read and hard to change and keep everything inside your head at once.- Solution:
Change your design. Split functions into multiple ones.
- Configuration:
This rule is configurable with
--max-returns
. Default: 5
New in version 0.1.0.
-
error_template
= 'Found too many return statements: {0}'¶
-
code
= 212¶
-
class
TooManyExpressionsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids putting too many expressions in a single function.
This rule is quite similar to “max lines” in a function, but is much nicer. Because we don’t count lines, we count real code entities. This way adding just several extra empty lines for readability will never trigger this violation.
Related to
OverusedExpressionViolation
.- Reasoning:
When there are too many expressions it means that this specific function does too many things at once. It has too much logic.
- Solution:
Split function into several functions, refactor your API.
- Configuration:
This rule is configurable with
--max-expressions
. Default: 9
New in version 0.1.0.
-
error_template
= 'Found too many expressions: {0}'¶
-
code
= 213¶
-
class
TooManyMethodsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have many methods in a single class.
- Reasoning:
Having too many methods might lead to the “God object”. This kind of objects can handle everything. So, in the end, your code becomes too hard to maintain and test.
- Solution:
What to do if you have too many methods in a single class? Split this class into several classes. Then use composition or inheritance to refactor your code. This will protect you from “God object” anti-pattern.
We do not make any difference between instance and class methods. We also do not care about functions and classes being public or not. We also do not count inherited methods from parents. This rule does not count the attributes of a class.
- Configuration:
This rule is configurable with
--max-methods
. Default: 7
New in version 0.1.0.
-
error_template
= 'Found too many methods: {0}'¶
-
code
= 214¶
-
class
TooManyBaseClassesViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Restrict the maximum number of base classes.
- Reasoning:
It is almost never possible to navigate to the desired method of a parent class when you need it with multiple mixins. It is hard to understand
mro
andsuper
calls. Do not overuse this technique.- Solution:
Reduce the number of base classes. Use composition over inheritance.
Example:
# Correct: class SomeClassName(First, Second, Mixin): ... # Wrong: class SomeClassName( FirstParentClass, SecondParentClass, ThirdParentClass, CustomClass, AddedClass, ): ...
- Configuration:
This rule is configurable with
--max-base-classes
. Default: 3
New in version 0.3.0.
Changed in version 0.5.0.
-
error_template
= 'Too many base classes: {0}'¶
-
code
= 215¶
-
class
TooManyDecoratorsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Restrict the maximum number of decorators.
- Reasoning:
When you are using too many decorators it means that you try to overuse the magic. You have to ask yourself: do I really know what happens inside this decorator tree? Typically, the answer will be “no”.
- Solution:
Using too many decorators typically means that you try to configure the behavior from outside of the class. Do not do that too much. Split functions or classes into multiple ones. Use higher order decorators.
- Configuration:
This rule is configurable with
--max-decorators
. Default: 5
This rule checks: functions, methods, and classes.
New in version 0.5.0.
-
error_template
= 'Too many decorators: {0}'¶
-
code
= 216¶
-
class
TooManyAwaitsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids placing too many
await
expressions into a function.- Reasoning:
When there are too many
await
keywords, functions are starting to get really complex. It is hard to tell where are we and what is going on.- Solution:
Change your design. Split functions into multiple ones.
- Configuration:
This rule is configurable with
--max-awaits
. Default: 5
New in version 0.10.0.
-
error_template
= 'Found too many await expressions: {0}'¶
-
code
= 217¶
-
class
TooManyAssertsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids placing too many
asseert
statements into a function.- Reasoning:
When there are too many
assert
keywords, functions are starting to get really complex. It might indicate that your tests or contracts are too big.- Solution:
Create rich
assert
statements, use higher-level contracts, or create special guard functions.- Configuration:
This rule is configurable with
--max-asserts
. Default: 5
New in version 0.12.0.
-
error_template
= 'Found too many `assert` statements: {0}'¶
-
code
= 218¶
-
class
TooDeepAccessViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have consecutive expressions with too deep access level.
We consider only these expressions as accesses:
ast.Subscript
ast.Attribute
We do not treat
ast.Call
as an access, since there are a lot of call-based APIs like Django ORM, builder patterns, etc.- Reasoning:
Having too deep access level indicates a bad design and overcomplicated data without proper API.
- Solution:
Split the expression into variables, functions or classes. Refactor the API for your data layout.
Example:
# Correct: access level = 4 self.attr.inner.wrapper[1] # Correct: access level = 1 manager.filter().exclude().annotate().values().first() # Wrong: access level = 5 self.attr.inner.wrapper.method.call() # Wrong: access level = 5 # `obj` has access level of 2: # `.attr`, `.call` # `call()` has access level of 5: # `.other`, `[0]`, `.field`, `.type`, `.boom` obj.attr.call().other[0].field.type.boom
- Configuration:
This rule is configurable with
--max-access-level
. Default: 4
New in version 0.12.0.
-
error_template
= 'Found too deep access level: {0}'¶
-
code
= 219¶
-
class
TooDeepNestingViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids nesting blocks too deep.
- Reasoning:
If nesting is too deep that indicates usage of complex logic and language constructions. This means that our design is not suited to handle such construction.
- Solution:
We need to refactor our complex construction into simpler ones. We can use new functions or different constructions.
New in version 0.1.0.
Changed in version 0.5.0.
-
error_template
= 'Found too deep nesting: {0}'¶
-
code
= 220¶
-
class
LineComplexityViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have complex lines.
We are using Jones Complexity algorithm to count complexity. What is Jones Complexity? It is a simple yet powerful method to count the number of
ast
nodes per line. If the complexity of a single line is higher than a threshold, then an error is raised.What nodes do we count? All except the following:
modules
function and classes, since they are checked differently
type annotations, since they do not increase the complexity
- Reasoning:
Having a complex line indicates that you somehow managed to put too much logic inside a single line. At some point in time, you will no longer be able to understand what this line means and what it does.
- Solution:
Split a single line into several lines: by creating new variables, statements or functions. Note, this might trigger new complexity issues. With this technique, a single new node in a line might trigger a complex refactoring process including several modules.
- Configuration:
This rule is configurable with
--max-line-complexity
. Default: 14
New in version 0.1.0.
-
error_template
= 'Found line with high Jones Complexity: {0}'¶
-
code
= 221¶
-
class
TooManyConditionsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have conditions with too many logical operators.
We use 4 as a default value.
- Reasoning:
When reading through the complex conditions you will fail to understand all the possible branches. And you will end up putting debug breakpoint on this line just to figure out how it works.
- Solution:
We can reduce the complexity of a single
if
by doing two things: creating new variables or creating nestedif
statements. Both of these actions will trigger other complexity checks.
We count
and
andor
keywords as conditions.New in version 0.1.0.
Changed in version 0.5.0.
-
error_template
= 'Found a condition with too much logic: {0}'¶
-
code
= 222¶
-
class
TooManyElifsViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to use many
elif
branches.We use 3 as a default value.
- Reasoning:
This rule is specifically important because of many
elif
branches indicate a complex flow in your design: you are reimplementingswitch
in python.- Solution:
There are different design patterns to use instead. For example, you can use some interface that just call a specific method without
if
. Or separate yourif
into multiple functions.
New in version 0.1.0.
Changed in version 0.5.0.
-
error_template
= 'Found too many `elif` branches: {0}'¶
-
code
= 223¶
-
class
TooManyForsInComprehensionViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have too many
for
statement within a comprehension.- Reasoning:
When reading through the complex comprehension you will fail to understand it.
- Solution:
We can reduce the complexity of comprehension by reducing the amount of
for
statements. Refactor your code to use severalfor
loops, comprehensions, or different functions.
Example:
# Wrong: ast_nodes = [ target for assignment in top_level_assigns for target in assignment.targets for _ in range(10) ]
New in version 0.3.0.
-
error_template
= 'Found a comprehension with too many `for` statements'¶
-
code
= 224¶
-
class
TooManyExceptCasesViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have too many
except
cases in a singletry
clause.We use 3 as a default value.
- Reasoning:
Handling too many exceptions in a single place is a good indicator of a bad design. Since this way, one controlling structure will become too complex. And you will need to test a lot of paths your application might go.
- Solution:
We can reduce the complexity of this case by splitting it into multiple
try
cases, functions or using a decorator to handle different exceptions.
New in version 0.7.0.
-
error_template
= 'Found too many `except` cases: {0}'¶
-
code
= 225¶
-
class
OverusedStringViolation
(node=None, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.MaybeASTViolation
Forbids to over-use string constants.
We allow to use strings without any restrictions as annotations for variables, arguments, return values, and class attributes.
- Reasoning:
When some string is used more than several time in your code, it probably means that this string is a meaningful constant. And should be treated like one.
- Solution:
Deduplicate you string usages by defining new functions or constants.
- Configuration:
This rule is configurable with
--max-string-usages
. Default: 3
New in version 0.10.0.
-
error_template
= 'Found string constant over-use: {0}'¶
-
code
= 226¶
-
class
TooLongYieldTupleViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to yield too long tuples.
- Reasoning:
Long yield tuples complicate generator using. This rule helps to reduce complication.
- Solution:
Use lists of similar type or wrapper objects.
New in version 0.10.0.
-
error_template
= 'Found too long yield tuple: {0}'¶
-
code
= 227¶
-
class
TooLongCompareViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have too long compare expressions.
- Reasoning:
To long compare expressions indicate that there’s something wrong going on in the code. Compares should not be longer than 3 or 4 items.
- Solution:
Use several conditions, seprate variables, or functions.
New in version 0.10.0.
-
error_template
= 'Found too long compare'¶
-
code
= 228¶
-
class
TooLongTryBodyViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have
try
blocks with too long bodies.- Reasoning:
Having too many statements inside your
try
block can lead to situations when some different statement raises an exception and you are not aware of it since it is not expected.- Solution:
Move things out of the
try
block or create new functions. The less lines you have in yourtry
block - the safer you are from accidental errors.- Configuration:
This rule is configurable with
--max-try-body-length
. Default: 1
New in version 0.12.0.
-
error_template
= 'Found too long ``try`` body length: {0}'¶
-
code
= 229¶
-
class
TooManyPublicAttributesViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have
try
blocks with too long bodies.We only check static definitions in a form of
self.public = ...
. We do not count parent attributes. We do not count properties. We do not count annotations. We do not count class attributes. We do not count duplicates.- Reasoning:
Having too many public instance attributes means that your class is too complex in terms of coupling. Other classes and functions will rely on these concrete fields instead of better abstraction layers.
- Solution:
Make some attributes protected. Split this class into several ones. If class is a Data Transfer Object, then use
@dataclass
decorator.- Configuration:
This rule is configurable with
--max-attributes
. Default: 6
New in version 0.12.0.
-
error_template
= 'Found too many public instance attributes: {0}'¶
-
code
= 230¶
-
class
CognitiveComplexityViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids to have functions with too high cognitive complexity.
- Reasoning:
People are not great at reading and iterpretating code in their heads. That’s why code with a lot of nested loops, conditions, exceptions handlers, and context managers is hard to read and understand.
- Solution:
Rewrite your code to be simplier. Use flat structures and conditions, remove nested loops.
- Configuration:
This rule is configurable with
--max-cognitive-score
. Default: 12
See also
https://en.wikipedia.org/wiki/Cognitive_complexity https://pypi.org/project/cognitive-complexity/ https://github.com/Melevir/flake8-cognitive-complexity
New in version 0.13.0.
-
error_template
= 'Found too high function cognitive complexity: {0}'¶
-
code
= 231¶
-
class
CognitiveModuleComplexityViolation
(node=None, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.SimpleViolation
Forbids to have modules with too high average cognitive complexity.
- Reasoning:
Modules with lots of functions might hide cognitive complexity inside many small and relatevely simple functions.
- Solution:
Rewrite your code to be simplier. Or use several modules.
- Configuration:
This rule is configurable with
--max-cognitive-average
. Default: 8
New in version 0.13.0.
-
error_template
= 'Found too high module cognitive complexity: {0}'¶
-
code
= 232¶
-
class
TooLongCallChainViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids too long call chains.
- Reasoning:
Too long call chains are overcomplicated and indicators of bad API design.
- Solution:
Split the expression into variables, functions or classes. Refactor the API to allow higher-level access to functions.
- Configuration:
This rule is configurable with
--max-call-level
. Default: 3
New in version 0.13.0.
-
error_template
= 'Found too lang call chain length: {0}'¶
-
code
= 233¶
-
class
TooComplexAnnotationViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids too complex annotations.
Annotation complexity is maximum annotation nesting level. Example:
List[int]
has complexity of 2 andTuple[List[Optional[str]], int]
has complexity of 4.- Reasoning:
Too complex annotations make your types unreadable. And make developers afraid of types.
- Solution:
Create type aliases. And use them a lot!
- Configuration:
This rule is configurable with
--max-annotation-complexity
. Default: 3
See also
https://mypy.readthedocs.io/en/stable/kinds_of_types.html#type-aliases https://github.com/best-doctor/flake8-annotations-complexity
New in version 0.14.0.
-
error_template
= 'Found too complex annotation: {0}'¶
-
code
= 234¶
-
class
TooManyImportedModuleMembersViolation
(node, text=None, baseline=None)[source]¶ Bases:
wemake_python_styleguide.violations.base.ASTViolation
Forbids
from ... import ...
with too many imported names.- Reasoning:
Importing too many names from one import is easy way to cause violation
WPS203
- too many imported names.- Solution:
Refactor the imports to import a common namespace. Something like
from package import module
and then use it likemodule.function()
.
Example:
# Correct: import module # 1 imported name # Wrong: from module import func1, func2, ..., funcN # N imported names
- Configuration:
This rule is configurable with
--max-import-from-members
. Default: 8
New in version 0.14.0.
-
error_template
= 'Found too many imported names from a module: {0}'¶
-
code
= 235¶