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¶
WPS400 — Restrict various control (such as magic) comments. |
|
WPS401 — Forbid empty doc comments ( |
|
WPS402 — Forbid too many |
|
WPS403 — Forbid too many |
|
WPS404 — Forbid complex defaults. |
|
WPS405 — Forbid anything other than |
|
WPS406 — Forbid anything other than |
|
WPS407 — Forbid mutable constants on a module level. |
|
WPS408 — Forbid using the same logical conditions in one expression. |
|
WPS409 — Forbid heterogeneous operators in one comparison. |
|
WPS410 — Forbid some module-level variables. |
|
WPS411 — Forbid empty modules. |
|
WPS412 — Forbid logic inside |
|
WPS413 — Forbid |
|
WPS414 — Forbid tuple unpacking with side-effects. |
|
WPS415 — Forbid the same exception class in multiple |
|
WPS416 — Forbid |
|
WPS417 — Forbid duplicate items in hashes. |
|
WPS418 — Forbid exceptions inherited from |
|
WPS419 — Forbid multiple returning paths with |
|
WPS420 — Forbid some |
|
WPS421 — Forbid calling some built-in functions. |
|
WPS422 — Forbid |
|
WPS423 — Forbid |
|
WPS424 — Forbid |
|
WPS425 — Forbid booleans as non-keyword parameters. |
|
WPS426 — Forbid |
|
WPS427 — Forbid unreachable code. |
|
WPS428 — Forbid statements that do nothing. |
|
WPS429 — Forbid multiple assignments on the same line. |
|
WPS430 — Forbid nested functions. |
|
WPS431 — Forbid nested classes. |
|
WPS432 — Forbid magic numbers. |
|
WPS433 — Forbid imports nested in functions. |
|
WPS434 — Forbid assigning a variable to itself. |
|
WPS435 — Forbid multiplying lists. |
|
WPS436 — Forbid importing protected modules. |
|
WPS437 — Forbid protected attributes and methods. |
|
WPS438 — Forbid raising |
|
WPS439 — Forbid Unicode escape sequences in binary strings. |
|
WPS440 — Forbid overlapping local and block variables. |
|
WPS441 — Forbid control variables after the block body. |
|
WPS442 — Forbid shadowing variables from outer scopes. |
|
WPS443 — Forbid explicit unhashable types of asset items and dict keys. |
|
WPS444 — Forbid explicit falsely-evaluated conditions with several keywords. |
|
WPS445 — Forbid incorrectly named keywords in starred dicts. |
|
WPS446 — Forbid approximate constants. |
|
WPS447 — Forbid using the alphabet as a string. |
|
WPS448 — Forbid incorrect order of |
|
WPS449 — Forbid |
|
WPS450 — Forbid importing protected objects from modules. |
|
WPS451 — Forbid positional only or |
|
WPS452 — Forbid |
|
WPS453 — Forbid executing a file with shebang incorrectly set. |
|
WPS454 — Forbid raising |
|
WPS455 — Forbids using non-trivial expressions as a parameter for |
|
WPS456 — Forbids using |
|
WPS457 — Forbids use of infinite |
|
WPS458 — Forbids to import from already imported modules. |
|
WPS459 — Forbids comparisons with |
|
WPS460 — Forbids to have single element destructuring. |
|
WPS461 — Forbids to use specific inline ignore violations. |
|
WPS462 — Forbids direct usage of multiline strings. |
|
WPS463 — Forbids to have functions starting with |
|
WPS464 — Forbid empty comments. |
|
WPS465 — Forbid comparisons between bitwise and boolean expressions. |
|
WPS466 — Forbid using complex grammar for using decorators. |
|
WPS467 — Forbid using a bare |
|
WPS468 — Forbid using a placeholder ( |
|
WPS469 — Forbid raising an exception from itself. |
|
WPS470 — Forbid kwarg unpacking in class definition. |
|
WPS471 — Forbid consecutive slices. |
|
WPS472 — Forbid getting first element using unpacking. |
|
WPS473 — Limit empty lines in functions or methods body. |
|
WPS474 — Do not allow importing the same object under different aliases. |
|
WPS475 — Do not use problematic function parameters. |
|
WPS476 — Do not use |
|
WPS477 — Forbid using TypeVarTuple after a TypeVar with default. |
|
WPS478 — Forbid using non strict slice operations. |
|
WPS479 — Forbid using multi-line formatted string with single and double quotes. |
|
WPS480 — Forbid using comments inside formatted strings. |
Best practices¶
- final class WrongMagicCommentViolation(node=None, text=None, baseline=None)[source]¶
Bases:
SimpleViolationWPS400 — Restrict various control (such as magic) comments.
We do not allow:
# noqacomment without specified violations# type: some_typecomments to specify a type fortyped_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.
# noqacomment is restricted because it can hide other violations.# type: some_typecomment is restricted because we can use type annotations instead.- Solution:
Use
# noqacomments with specified error types. Use type annotations to specify types.
We still allow using
# type: ignorecomment, 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.'¶
- final class WrongDocCommentViolation(node, text=None, baseline=None)[source]¶
Bases:
TokenizeViolationWPS401 — 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
pycodestylechecks.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 (``#:``).'¶
- final class OveruseOfNoqaCommentViolation(node=None, text=None, baseline=None)[source]¶
Bases:
SimpleViolationWPS402 — Forbid too many
# noqacomments.We count them on a per-module basis.
- Reasoning:
Having too many
# noqacomments 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.'¶
- final class OveruseOfNoCoverCommentViolation(node=None, text=None, baseline=None)[source]¶
Bases:
SimpleViolationWPS403 — Forbid too many
# pragma: no covercomments.We count them on a per-module basis. We use 5 as a default value.
- Reasoning:
Having too many
# pragma: no covercomments 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.'¶
- final class ComplexDefaultValueViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS404 — Forbid complex defaults.
Anything that is not a
ast.Name,ast.Attribute,ast.Tuple, orast.Constantshould 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¶
- full_code: ClassVar[str] = 'WPS404'¶
- summary: ClassVar[str] = 'Forbid complex defaults.'¶
- final class LoopVariableDefinitionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS405 — Forbid anything other than
ast.Nameto define loop variables.- Reasoning:
When defining a
forloop with attributes, indexes, calls, or any other nodes it does dirty things inside.- Solution:
Use regular
ast.Namevariables. Or tuple ofast.Namevariables. 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¶
- full_code: ClassVar[str] = 'WPS405'¶
- summary: ClassVar[str] = 'Forbid anything other than ``ast.Name`` to define loop variables.'¶
- final class ContextManagerVariableDefinitionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS406 — Forbid anything other than
ast.Nameto define contexts.- Reasoning:
When defining a
withcontext managers with attributes, indexes, calls, or any other nodes it does dirty things inside.- Solution:
Use regular
ast.Namevariables. Or tuple ofast.Namevariables. 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¶
- full_code: ClassVar[str] = 'WPS406'¶
- summary: ClassVar[str] = 'Forbid anything other than ``ast.Name`` to define contexts.'¶
- final class MutableModuleConstantViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS407 — 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.Listand 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¶
- full_code: ClassVar[str] = 'WPS407'¶
- summary: ClassVar[str] = 'Forbid mutable constants on a module level.'¶
- final class SameElementsInConditionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS408 — 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¶
- full_code: ClassVar[str] = 'WPS408'¶
- summary: ClassVar[str] = 'Forbid using the same logical conditions in one expression.'¶
- final class HeterogeneousCompareViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS409 — 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
andboolean 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¶
- full_code: ClassVar[str] = 'WPS409'¶
- summary: ClassVar[str] = 'Forbid heterogeneous operators in one comparison.'¶
- final class WrongModuleMetadataViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS410 — 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, orpyproject.toml. Use proper docstrings and packaging classifiers. Useimportlib.metadataif you need to import this data into your app.- Configuration:
This rule is configurable with
--allowed-module-metadata. Default: ()And with
--forbidden-module-metadata. Default: ()
Example:
# Correct: GLOBAL_CONSTANT: Final = 1 # 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.'¶
- final class EmptyModuleViolation(node=None, text=None, baseline=None)[source]¶
Bases:
SimpleViolationWPS411 — 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:
delete it
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.'¶
- final class InitModuleHasLogicViolation(node=None, text=None, baseline=None)[source]¶
Bases:
SimpleViolationWPS412 — Forbid logic inside
__init__module.- Reasoning:
If you have logic inside the
__init__module It means several things:you are keeping some outdated stuff there, you need to refactor
you are placing this logic in the wrong file, just create another one
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:comments, since they are dropped before AST comes in play
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.
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.'¶
- final class BadMagicModuleFunctionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS413 — 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.
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.'¶
- final class WrongUnpackingViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS414 — 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¶
- full_code: ClassVar[str] = 'WPS414'¶
- summary: ClassVar[str] = 'Forbid tuple unpacking with side-effects.'¶
- final class DuplicateExceptionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS415 — Forbid the same exception class in multiple
exceptblocks.- 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeB025.- error_template: ClassVar[str] = 'Found duplicate exception: {0}'¶
- code: ClassVar[int] = 415¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS415'¶
- summary: ClassVar[str] = 'Forbid the same exception class in multiple ``except`` blocks.'¶
- final class YieldInComprehensionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS416 — Forbid
yieldkeyword inside comprehensions.This is a
SyntaxErrorstarting frompython3.8.- Reasoning:
Having the
yieldkeyword inside comprehensions is error-prone. You can shoot yourself in the foot by an inaccurate usage of this feature.- Solution:
Use regular
forloops withyieldkeywords or create a separate generator function.
Example:
# Wrong: list((yield letter) for letter in 'ab') # Will result 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¶
- disabled_since: ClassVar[str | None] = '0.18.0'¶
- full_code: ClassVar[str] = 'WPS416'¶
- summary: ClassVar[str] = 'Forbid ``yield`` keyword inside comprehensions.'¶
- final class NonUniqueItemsInHashViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS417 — Forbid duplicate items in hashes.
- Reasoning:
When you explicitly put duplicate items in
setliterals or indictkeys 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeB033.- error_template: ClassVar[str] = 'Found non-unique item in hash: {0}'¶
- code: ClassVar[int] = 417¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS417'¶
- summary: ClassVar[str] = 'Forbid duplicate items in hashes.'¶
- final class BaseExceptionSubclassViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS418 — Forbid exceptions inherited from
BaseException.- Reasoning:
BaseExceptionis a special case: it is not designed to be extended by users. A lot of yourexcept Exceptioncases 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¶
- full_code: ClassVar[str] = 'WPS418'¶
- summary: ClassVar[str] = 'Forbid exceptions inherited from ``BaseException``.'¶
- final class TryExceptMultipleReturnPathViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS419 — Forbid multiple returning paths with
try/exceptcase.Note, that we check for any
return,break, orraisenodes.- Reasoning:
The problem with
returninelseandfinallyis that it is impossible to say what value is going to be returned without looking up the implementation details. Why? Becausereturndoes not expect that some other code will be executed after it. But,finallyis always executed, even afterreturn. Andelsewill not be executed when there are no exceptions intrycase and areturnstatement.- Solution:
Remove
returnfrom 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeB012andSIM107.- error_template: ClassVar[str] = 'Found `try`/`else`/`finally` with multiple return paths'¶
- code: ClassVar[int] = 419¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS419'¶
- summary: ClassVar[str] = 'Forbid multiple returning paths with ``try`` / ``except`` case.'¶
- final class WrongKeywordViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS420 — Forbid some
pythonkeywords.- Reasoning:
Using some keywords generally causes more pain than it relieves.
delkeyword 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 thatdelis actually used to nullify variables and delete them from the execution scope. Moreover, it has a lot of substitutions. You won’t miss it!passkeyword is just useless by design. There’s no use-case for it. Because it does literally nothing.globalandnonlocalpromote 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.
passshould be replaced with docstring orcontextlib.suppress.delshould be replaced with specialized methods like.pop().globalandnonlocalusages should be refactored.
Added in version 0.1.0.
Changed in version 1.0.0: Allows
passincasebodies.- error_template: ClassVar[str] = 'Found wrong keyword: {0}'¶
- code: ClassVar[int] = 420¶
- full_code: ClassVar[str] = 'WPS420'¶
- summary: ClassVar[str] = 'Forbid some ``python`` keywords.'¶
- final class WrongFunctionCallViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS421 — 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_BLACKLISTfor 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.'¶
- final class FutureImportViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS422 — Forbid
__future__imports.- Reasoning:
Almost all
__future__imports are legacypython2compatibility tools that are no longer required.- Solution:
Remove them. Drop
python2support.
Except, there are some new ones for
python4support. SeeFUTURE_IMPORTS_WHITELISTfor 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.'¶
- final class RaiseNotImplementedViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS423 — Forbid
NotImplementedexception.- Reasoning:
NotImplementedandNotImplementedErrorlook similar but they have different use cases. Use cases ofNotImplementedare 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeF901.- error_template: ClassVar[str] = 'Found raise NotImplemented'¶
- code: ClassVar[int] = 423¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS423'¶
- summary: ClassVar[str] = 'Forbid ``NotImplemented`` exception.'¶
- final class BaseExceptionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS424 — Forbid
BaseExceptionexception.- 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, andSystemExitseparately. Do not use the plainexcept:keyword.
Example:
# Correct: except Exception as ex: ... # Wrong: except BaseException as ex: ...
Added in version 0.3.0.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeBLE001.See also
https://docs.python.org/3/library/exceptions.html#exception-hierarchy https://help.semmle.com/wiki/pages/viewpage.action?pageId=1608527
- error_template: ClassVar[str] = 'Found except `BaseException`'¶
- code: ClassVar[int] = 424¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS424'¶
- summary: ClassVar[str] = 'Forbid ``BaseException`` exception.'¶
- final class BooleanPositionalArgumentViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS425 — 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeFBT003.- error_template: ClassVar[str] = 'Found boolean non-keyword argument: {0}'¶
- code: ClassVar[int] = 425¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS425'¶
- summary: ClassVar[str] = 'Forbid booleans as non-keyword parameters.'¶
- final class LambdaInsideLoopViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS426 — Forbid
lambdainside loops.We check
while,for, andasync forloop bodies. We also check comprehension value parts.- Reasoning:
It is error-prone to use
lambdainsideforandwhileloops due to the famous late-binding.- Solution:
Use regular functions, factory functions, or
partialfunctions. 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¶
- full_code: ClassVar[str] = 'WPS426'¶
- summary: ClassVar[str] = 'Forbid ``lambda`` inside loops.'¶
- final class UnreachableCodeViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS427 — 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
returnorraisestatements. However, we cannot cover 100% of truly unreachable code by this rule. This happens due to the dynamic nature of python. For example, detecting that1 / some_valuewould 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¶
- full_code: ClassVar[str] = 'WPS427'¶
- summary: ClassVar[str] = 'Forbid unreachable code.'¶
- final class StatementHasNoEffectViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS428 — 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.Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeB015andB018.- error_template: ClassVar[str] = 'Found statement that has no effect'¶
- code: ClassVar[int] = 428¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS428'¶
- summary: ClassVar[str] = 'Forbid statements that do nothing.'¶
- final class MultipleAssignmentsViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS429 — 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¶
- full_code: ClassVar[str] = 'WPS429'¶
- summary: ClassVar[str] = 'Forbid multiple assignments on the same line.'¶
- final class NestedFunctionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS430 — 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
lambdaandasyncfunctions.See
NESTED_FUNCTIONS_WHITELISTfor 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.'¶
- final class NestedClassViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS431 — 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.'¶
- final class MagicNumberViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS432 — 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. Or0.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.
intnumbers that are in range[-10, 10]and some other common numbers, that are defined inMAGIC_NUMBERS_WHITELISTAdded 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.'¶
- final class NestedImportViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS433 — 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
ruffandpylintlinters. SeePLC0415.- error_template: ClassVar[str] = 'Found nested import'¶
- code: ClassVar[int] = 433¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS433'¶
- summary: ClassVar[str] = 'Forbid imports nested in functions.'¶
- final class ReassigningVariableToItselfViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS434 — 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
ruffandpylintlinters. SeePLW0127.- error_template: ClassVar[str] = 'Found reassigning variable to itself: {0}'¶
- code: ClassVar[int] = 434¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS434'¶
- summary: ClassVar[str] = 'Forbid assigning a variable to itself.'¶
- final class ListMultiplyViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS435 — 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.'¶
- final class ProtectedModuleViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS436 — 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
ruffandpylintlinters. SeePLC2701.- error_template: ClassVar[str] = 'Found protected module import: {0}'¶
- code: ClassVar[int] = 436¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS436'¶
- summary: ClassVar[str] = 'Forbid importing protected modules.'¶
- final class ProtectedAttributeViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS437 — 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, andsuper()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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeSLF001.- error_template: ClassVar[str] = 'Found protected attribute usage: {0}'¶
- code: ClassVar[int] = 437¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS437'¶
- summary: ClassVar[str] = 'Forbid protected attributes and methods.'¶
- final class StopIterationInsideGeneratorViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS438 — Forbid raising
StopIterationinside generators.- Reasoning:
StopIterationshould 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.'¶
- final class WrongUnicodeEscapeViolation(node, text=None, baseline=None)[source]¶
Bases:
TokenizeViolationWPS439 — 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.'¶
- final class BlockAndLocalOverlapViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS440 — Forbid overlapping local and block variables.
What we call local variables:
Assigns and annotations
Function arguments (they are local to the function body)
What we call block variables:
Imports
Functions and async functions definitions
Classes, methods, and async methods definitions
For and async for loops variables
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.
Changed in version 1.0.0: Disabled. This rule was buggy and not really useful. It produced a lot of false positives and did not really find any problems.
This rule now can be partially replaced with
mypywithpossibly-undefinederror code enabled.- error_template: ClassVar[str] = 'Found block variables overlap: {0}'¶
- code: ClassVar[int] = 440¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS440'¶
- summary: ClassVar[str] = 'Forbid overlapping local and block variables.'¶
- final class ControlVarUsedAfterBlockViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS441 — Forbid control variables after the block body.
What we call block control variables:
forloop unpacked variableswithcontext 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.
Changed in version 1.0.0: Allows using variables after blocks in
assertstatements.- 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.'¶
- final class OuterScopeShadowingViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS442 — 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.
Changed in version 1.0.0: Disabled. This rule was buggy and not really useful. It produced a lot of false positives and did not really find any problems.
This rule now can be partially replaced with
mypywithpossibly-undefinederror code enabled.- error_template: ClassVar[str] = 'Found outer scope names shadowing: {0}'¶
- code: ClassVar[int] = 442¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS442'¶
- summary: ClassVar[str] = 'Forbid shadowing variables from outer scopes.'¶
- final class UnhashableTypeInHashViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS443 — Forbid explicit unhashable types of asset items and dict keys.
- Reasoning:
This will resolve in
TypeErrorin 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.'¶
- final class WrongKeywordConditionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS444 — Forbid explicit falsely-evaluated conditions with several keywords.
We check:
ast.Whileast.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.'¶
- final class WrongNamedKeywordViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS445 — 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.'¶
- final class ApproximateConstantViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS446 — 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_CONSTANTSfor 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.'¶
- final class StringConstantRedefinedViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS447 — 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.'¶
- final class IncorrectExceptOrderViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS448 — 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: ...
See also
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``.'¶
- final class FloatKeyViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS449 — Forbid
floatkeys.- Reasoning:
floatis a very ugly data type. It has a lot of “precision” errors. When we usefloatas keys we can hit this wall. Moreover, we cannot usefloatkeys 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.
Changed in version 1.0.0: Does not eval nodes anymore to get
floatinstances from math operations. It was slow and not very helpful.- 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.'¶
- final class ProtectedModuleMemberViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS450 — 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
ruffandpylintlinters. SeePLC2701.- error_template: ClassVar[str] = 'Found protected object import: {0}'¶
- code: ClassVar[int] = 450¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS450'¶
- summary: ClassVar[str] = 'Forbid importing protected objects from modules.'¶
- final class PositionalOnlyArgumentsViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS451 — 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¶
- disabled_since: ClassVar[str | None] = '0.19.0'¶
- full_code: ClassVar[str] = 'WPS451'¶
- summary: ClassVar[str] = 'Forbid positional only or ``/`` arguments.'¶
- final class LoopControlFinallyViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS452 — Forbid
breakandcontinuein afinallyblock.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
breakandcontinuefromfinallyblocks.
Example:
# Correct: try: ... finally: ... # Wrong: try: ... finally: break try: ... finally: continue
Added in version 0.14.0.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeB012.- error_template: ClassVar[str] = 'Found `break` or `continue` in `finally` block'¶
- code: ClassVar[int] = 452¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS452'¶
- summary: ClassVar[str] = 'Forbid ``break`` and ``continue`` in a ``finally`` block.'¶
- final class ShebangViolation(node=None, text=None, baseline=None)[source]¶
Bases:
SimpleViolationWPS453 — 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/python #!/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.'¶
- final class BaseExceptionRaiseViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS454 — Forbid raising
ExceptionorBaseException.- Reasoning:
ExceptionandBaseExceptionare 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")
See also
https://docs.python.org/3/library/exceptions.html#exception-hierarchy https://docs.python.org/3/tutorial/errors.html#user-defined-exceptions
Added in version 0.15.0.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
rufflinter. SeeTRY003.- error_template: ClassVar[str] = 'Found wrong `raise` exception type: {0}'¶
- code: ClassVar[int] = 454¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS454'¶
- summary: ClassVar[str] = 'Forbid raising ``Exception`` or ``BaseException``.'¶
- final class NonTrivialExceptViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS455 — Forbids using non-trivial expressions as a parameter for
except.- Reasoning:
Expressions used as an argument for
exceptcould be hard to read and hide real list of exceptions being expected to occur in the outlined code block.- Solution:
Use separate
exceptblocks for each exception or provide a tuple of exception classes.
Example:
# Correct: try: ... except ValueError: ... except TypeError: ... try: ... except (TypeError, ValueError): ... # Wrong: try: ... except produce_excs(): ...
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``.'¶
- final class FloatingNanViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS456 — 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.naninstead.
Example:
# Correct: min(math.nan, 3) # Wrong: min(float("NAN"), 3)
Added in version 0.15.0.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
ruffandpylintlinters. SeePLW0177.- error_template: ClassVar[str] = 'Found "NaN" as argument to float()'¶
- code: ClassVar[int] = 456¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS456'¶
- summary: ClassVar[str] = 'Forbids using ``float("NaN")`` construct to generate NaN.'¶
- final class InfiniteWhileLoopViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS457 — 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.'¶
- final class ImportCollisionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS458 — 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.'¶
- final class FloatComplexCompareViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS459 — Forbids comparisons with
floatandcomplex.- Reasoning:
This is a best practice rule, as
floatandcomplexsuffer from representation error, leading to possibly incorrect results during comparison.- Solution:
Use fuzzy operators. 1.
abs(f1 - f2) <= allowed_error2.math.isclose(f1, f2)(forfloat) 3.cmath.isclose(c1, c2)(forcomplex) 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``.'¶
- final class SingleElementDestructuringViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS460 — 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.'¶
- final class ForbiddenInlineIgnoreViolation(node=None, text=None, baseline=None)[source]¶
Bases:
SimpleViolationWPS461 — 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.'¶
- final class WrongMultilineStringUseViolation(node, text=None, baseline=None)[source]¶
Bases:
TokenizeViolationWPS462 — Forbids 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.
Changed in version 1.0.0: Relaxed this rule.
- error_template: ClassVar[str] = 'Wrong multiline string usage'¶
- code: ClassVar[int] = 462¶
- full_code: ClassVar[str] = 'WPS462'¶
- summary: ClassVar[str] = 'Forbids direct usage of multiline strings.'¶
- final class GetterWithoutReturnViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS463 — 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
returnoryielda 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.'¶
- final class EmptyCommentViolation(node, text=None, baseline=None)[source]¶
Bases:
TokenizeViolationWPS464 — 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.'¶
- final class BitwiseAndBooleanMixupViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS465 — Forbid comparisons between bitwise and boolean expressions.
Empty comments are only allowed in between valid comments.
- Reasoning:
This case indicates that a person confused
&withandand|withor. 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.
Changed in version 1.0.0: No longer produced, kept here for historic reasons.
- error_template: ClassVar[str] = 'Found likely bitwise and boolean operation mixup'¶
- code: ClassVar[int] = 465¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS465'¶
- summary: ClassVar[str] = 'Forbid comparisons between bitwise and boolean expressions.'¶
- final class NewStyledDecoratorViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS466 — 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 with generic type specifications as decorators only. You are free to pass any args to function calls, however.
Example:
# Correct: @some.decorator(args) def my_function(): ... # Only for `python3.12+` @MyClassDecorator[my_type](args) def my_function(): ... # Wrong: @some.decorator + other.decorator def my_function(): ... @some.dict_decorators['method'] def my_function(): ... @some.list_decorators[index] 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.'¶
- final class BareRaiseViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS467 — Forbid using a bare
raisekeyword outside ofexcept.- Reasoning:
Using a bare
raiseoutside of anexceptblock causes a runtime error.- Solution:
Only use bare
raisewithin anexceptblock.
Example:
# Correct: def smth(): try: ... except: raise # Wrong: def smth(): raise
Added in version 0.16.0.
Changed in version 1.0.0: No longer produced, kept here for historic reasons. This is covered with
ruffandpylintlinters. SeePLE0704.- error_template: ClassVar[str] = 'Found bare raise keyword'¶
- code: ClassVar[int] = 467¶
- disabled_since: ClassVar[str | None] = '1.0.0'¶
- full_code: ClassVar[str] = 'WPS467'¶
- summary: ClassVar[str] = 'Forbid using a bare ``raise`` keyword outside of ``except``.'¶
- final class RedundantEnumerateViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS468 — Forbid using a placeholder (
_) withenumerate.- Reasoning:
This adds no value and introduces additional complexity.
- Solution:
Only use
enumeratewhen 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``.'¶
- final class RaiseFromItselfViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS469 — 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.'¶
- final class KwargsUnpackingInClassDefinitionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS470 — 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.'¶
- final class ConsecutiveSlicesViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS471 — 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.'¶
- final class GettingElementByUnpackingViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS472 — 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.'¶
- final class WrongEmptyLinesCountViolation(node, text=None, baseline=None)[source]¶
Bases:
TokenizeViolationWPS473 — 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.'¶
- final class ImportObjectCollisionViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS474 — 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.'¶
- final class ProblematicFunctionParamsViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS475 — Do not use problematic function parameters.
- Patterns that we don’t allow:
More than one pos-only params with defaults, you cannot really pass the second one without passing the first one anyway
Pos-only or regular param with default before
*args, this way you won’t be able to pass just*args
- Reasoning:
It would be hard to pass arguments to these functions.
- Solution:
Change the signature: remove defaults or change the parameter kinds.
Example:
# Correct: def function(first, second): ... # Wrong: def function(first=0, second=1, /): ... def function(first=0, *args): ...
Added in version 1.0.0.
- error_template: ClassVar[str] = 'Found problematic function parameters'¶
- code: ClassVar[int] = 475¶
- full_code: ClassVar[str] = 'WPS475'¶
- summary: ClassVar[str] = 'Do not use problematic function parameters.'¶
- final class AwaitInLoopViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS476 — Do not use
awaitinforloop.- Reasoning:
There is a better way to control repeated coroutines in
forloops.- Solution:
Use
asyncio.gather(),asyncio.wait(), orasyncio.TaskGroup
Example:
# Correct: async def request(): tasks = [parse_content(url) for url in urls] parsed_content = await asyncio.gather(*tasks) # Wrong: async def request(): parsed_content = [] for url in urls: result = await parse_content(url) parsed_content.append(result)
Added in version 1.1.0.
- error_template: ClassVar[str] = 'Found `await` in `for` loop'¶
- code: ClassVar[int] = 476¶
- full_code: ClassVar[str] = 'WPS476'¶
- summary: ClassVar[str] = 'Do not use ``await`` in ``for`` loop.'¶
- final class SneakyTypeVarWithDefaultViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS477 — Forbid using TypeVarTuple after a TypeVar with default.
- Reasoning:
Following a defaulted TypeVar with a TypeVarTuple is bad, because you cannot specify the TypeVarTuple without specifying the TypeVar.
- Solution:
Consider refactoring and getting rid of this pattern.
Example:
# Wrong: class Class[T=int, *Ts=*tuple[int, ...]]: ... # Correct (no default): class Class[T, *Ts]: ... # Correct (no tuple): class Class[T=int]: ...
Added in version 1.1.0.
- error_template: ClassVar[str] = 'Found a TypeVarTuple following a TypeVar with default'¶
- code: ClassVar[int] = 477¶
- full_code: ClassVar[str] = 'WPS477'¶
- summary: ClassVar[str] = 'Forbid using TypeVarTuple after a TypeVar with default.'¶
- final class NonStrictSliceOperationsViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS478 — Forbid using non strict slice operations.
- Reasoning:
We have two ways to do something.
- Solution:
Prefer a more descriptive way.
Example:
# Correct: items.reverse() ''.join(reversed('abc')) items.copy() # Wrong: items[::-1] # `.reverse()` or `reversed()` items[:] # `.copy()` or `copy.copy()`
Added in version 1.2.0.
- error_template: ClassVar[str] = 'Found non strict slice operation'¶
- code: ClassVar[int] = 478¶
- full_code: ClassVar[str] = 'WPS478'¶
- summary: ClassVar[str] = 'Forbid using non strict slice operations.'¶
- final class MultilineFormattedStringViolation(node, text=None, baseline=None)[source]¶
Bases:
TokenizeViolationWPS479 — Forbid using multi-line formatted string with single and double quotes.
- Reasoning:
Multiline f-strings must use triple quotes for clarity. Single f-strings may not span lines.
- Solution:
Use triple quotes instead of single quotes.
Example:
# Correct x = f''' { 1 ...}''' # Wrong: x = f' { 1 ...}'
Added in version 1.2.0.
- error_template: ClassVar[str] = 'Found multi-line formatted string'¶
- code: ClassVar[int] = 479¶
- full_code: ClassVar[str] = 'WPS479'¶
- summary: ClassVar[str] = 'Forbid using multi-line formatted string with single and double quotes.'¶
- final class CommentInFormattedStringViolation(node, text=None, baseline=None)[source]¶
Bases:
TokenizeViolationWPS480 — Forbid using comments inside formatted strings.
Is only emitted on
python3.12+.- Reasoning:
Comments make fstring implicitly multiline. And comments must not be present in strings. This is not right.
- Solution:
Don’t write comments inside fstrings.
Example:
# Correct: element = f'<p>{content}</p>' # Create html element # Wrong: element = f'<p>{content # Create html element }'Added in version 1.2.0.
- error_template: ClassVar[str] = 'Found comment inside formatted string'¶
- code: ClassVar[int] = 480¶
- full_code: ClassVar[str] = 'WPS480'¶
- summary: ClassVar[str] = 'Forbid using comments inside formatted strings.'¶