OOP¶
These checks ensures that you use Python’s version of OOP correctly.
There are different gotchas in Python to write beautiful classes and using objects correctly. That’s the place we collect these kind of rules.
Summary¶
WPS600 — Forbid subclassing lowercase builtins. |
|
WPS601 — Forbid shadowing class level attributes with instance level attributes. |
|
WPS602 — Forbid |
|
WPS603 — Forbid certain magic methods. |
|
WPS604 — Forbid incorrect nodes inside |
|
WPS605 — Forbid methods without any arguments. |
|
WPS606 — Forbid anything other than a class as a base class. |
|
WPS607 — Forbid incorrect |
|
WPS608 — Forbid |
|
WPS609 — Forbid directly calling certain magic attributes and methods. |
|
WPS610 — Forbid certain async magic methods. |
|
WPS611 — Forbid |
|
WPS612 — Forbid useless overwritten methods. |
|
WPS613 — Forbid |
|
WPS614 — Forbids descriptors in regular functions. |
|
WPS615 — Forbids to use getters and setters in objects. |
Respect your objects¶
- class BuiltinSubclassViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS600 — Forbid subclassing lowercase builtins.
We forbid to subclass builtins like
int,str,bool, etc. We allow to subclassobjectandtype, warnings, and exceptions.See
ALLOWED_BUILTIN_CLASSESfor the whole list of whitelisted names.- Reasoning:
It is almost never a good idea (unless you do something sneaky) to subclass primitive builtins.
- Solution:
Use custom objects around some wrapper. Use magic methods to emulate the desired behaviour.
Example:
# Correct: class Some(object): ... class MyValueException(ValueError): ... # Wrong: class MyInt(int): ...
New in version 0.10.0.
Changed in version 0.11.0.
- error_template: ClassVar[str] = 'Found subclassing a builtin: {0}'¶
- code: ClassVar[int] = 600¶
- previous_codes: ClassVar[Set[int]] = {426}¶
- full_code: ClassVar[str] = 'WPS600'¶
- summary: ClassVar[str] = 'Forbid subclassing lowercase builtins.'¶
- class ShadowedClassAttributeViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS601 — Forbid shadowing class level attributes with instance level attributes.
- Reasoning:
This way you will have two attributes inside your
__mro__chain: one from instance and one from class. It might cause errors. Needless to say, that this is just pointless to do so.Also, if you ever want to optimise your code with a tool like mypyc, this rule is a requirement.
- Solution:
Use either class attributes or instance attributes. Use
ClassVartype on fields that are declared as class attributes.
Note, that we cannot find shadowed attributes that are defined in parent classes. That’s where
ClassVaris required formypyto check it for you.Example:
# Correct: from typing import ClassVar class First(object): field: ClassVar[int] = 1 class Second(object): field: int def __init__(self) -> None: self.field = 1 # Wrong: class Some(object): field = 1 def __init__(self) -> None: self.field = 1
New in version 0.10.0.
Changed in version 0.11.0.
Changed in version 0.14.0.
- error_template: ClassVar[str] = 'Found shadowed class attribute: {0}'¶
- code: ClassVar[int] = 601¶
- previous_codes: ClassVar[Set[int]] = {427}¶
- full_code: ClassVar[str] = 'WPS601'¶
- summary: ClassVar[str] = 'Forbid shadowing class level attributes with instance level attributes.'¶
- class StaticMethodViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS602 — Forbid
@staticmethoddecorator.- Reasoning:
Static methods are not required to be inside the class. Because they even do not have access to the current instance.
- Solution:
Use instance methods,
@classmethod, or functions instead.
New in version 0.1.0.
Changed in version 0.11.0.
See also
webucator.com/article/when-to-use-static-methods-in-python-never
- error_template: ClassVar[str] = 'Found using `@staticmethod`'¶
- code: ClassVar[int] = 602¶
- previous_codes: ClassVar[Set[int]] = {433}¶
- full_code: ClassVar[str] = 'WPS602'¶
- summary: ClassVar[str] = 'Forbid ``@staticmethod`` decorator.'¶
- class BadMagicMethodViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS603 — Forbid certain magic methods.
- Reasoning:
We forbid to use magic methods related to the forbidden language parts. Likewise, we forbid to use
delkeyword, so we forbid to use all magic methods related to it.- Solution:
Refactor your code to use custom methods instead. It will give more context to your app.
See
MAGIC_METHODS_BLACKLISTfor the full blacklist of the magic methods.New in version 0.1.0.
Changed in version 0.11.0.
- error_template: ClassVar[str] = 'Found using restricted magic method: {0}'¶
- code: ClassVar[int] = 603¶
- previous_codes: ClassVar[Set[int]] = {434}¶
- full_code: ClassVar[str] = 'WPS603'¶
- summary: ClassVar[str] = 'Forbid certain magic methods.'¶
- class WrongClassBodyContentViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS604 — Forbid incorrect nodes inside
classdefinitions.- Reasoning:
Python allows us to have conditions, context managers, and even infinite loops inside
classdefinitions. On the other hand, only methods, attributes, and docstrings make sense. So, we discourage using anything except these nodes in class bodies.- Solution:
If you have complex logic inside your class definition, most likely that you do something wrong. There are different options to refactor this mess. You can try metaclasses, decorators, builders, and other patterns.
Example:
# Wrong: class Test(object): for _ in range(10): print('What?!')
We also allow some nested classes, check out
NestedClassViolationfor more information.New in version 0.7.0.
Changed in version 0.11.0.
- error_template: ClassVar[str] = 'Found incorrect node inside `class` body'¶
- code: ClassVar[int] = 604¶
- previous_codes: ClassVar[Set[int]] = {452}¶
- full_code: ClassVar[str] = 'WPS604'¶
- summary: ClassVar[str] = 'Forbid incorrect nodes inside ``class`` definitions.'¶
- class MethodWithoutArgumentsViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS605 — Forbid methods without any arguments.
- Reasoning:
Methods without arguments are allowed to be defined, but almost impossible to use. Furthermore, they don’t have an access to
self, so cannot access the inner state of the object. It might be an intentional design or just a typo.- Solution:
Move any methods with arguments to raw functions. Or just add an argument if it is actually required.
Example:
# Correct: class Test(object): def method(self): ... # Wrong: class Test(object): def method(): ...
New in version 0.7.0.
Changed in version 0.11.0.
- error_template: ClassVar[str] = 'Found method without arguments: {0}'¶
- code: ClassVar[int] = 605¶
- previous_codes: ClassVar[Set[int]] = {453}¶
- full_code: ClassVar[str] = 'WPS605'¶
- summary: ClassVar[str] = 'Forbid methods without any arguments.'¶
- class WrongBaseClassViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS606 — Forbid anything other than a class as a base class.
We only check base classes and not keywords. They can be anything you need.
- Reasoning:
In Python you can specify anything in the base classes slot. In runtime this expression will be evaluated and executed. We need to prevent dirty hacks in this field.
- Solution:
Use only attributes, names, and types to be your base classes. Use
annotationfuture import in case you use strings in base classes.
Example:
# Correct: class Test(module.ObjectName, MixinName, keyword=True): ... class GenericClass(Generic[ValueType]): ... # Wrong: class Test((lambda: object)()): ...
New in version 0.7.0.
Changed in version 0.7.1.
Changed in version 0.11.0.
Changed in version 0.12.0.
- error_template: ClassVar[str] = 'Found incorrect base class'¶
- code: ClassVar[int] = 606¶
- previous_codes: ClassVar[Set[int]] = {454}¶
- full_code: ClassVar[str] = 'WPS606'¶
- summary: ClassVar[str] = 'Forbid anything other than a class as a base class.'¶
- class WrongSlotsViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS607 — Forbid incorrect
__slots__definition.Things that this rule checks:
That
__slots__is a tuple, name, attribute, star, or callThat
__slots__do not have duplicatesThat
__slots__do not have empty strings or invalid python names
- Reasoning:
__slots__is a very special attribute. It completely changes your class. So, we need to be careful with it. We should not allow anything rather than tuples to define slots, we also need to check that fields defined in__slots__are unique.- Solution:
Use tuples with unique elements to define
__slots__attribute. Usesnake_caseto define attributes in__slots__.
Example:
# Correct: class Test(object): __slots__ = ('field1', 'field2') class Other(Test): __slots__ = (*Test.__slots__, 'child') # Wrong: class Test(object): __slots__ = ['field1', 'field2', 'field2']
Note, that we do ignore all complex expressions for this field. So, we only check raw literals.
New in version 0.7.0.
Changed in version 0.11.0.
Changed in version 0.12.0.
- error_template: ClassVar[str] = 'Found incorrect `__slots__` syntax'¶
- code: ClassVar[int] = 607¶
- previous_codes: ClassVar[Set[int]] = {455}¶
- full_code: ClassVar[str] = 'WPS607'¶
- summary: ClassVar[str] = 'Forbid incorrect ``__slots__`` definition.'¶
- class WrongSuperCallViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS608 — Forbid
super()with parameters or outside of methods.- Reasoning:
super()is a very special function. It implicitly relies on the context where it is used and parameters passed to it. So, we should be very careful with parameters and context.- Solution:
Use
super()without arguments and only inside methods.
Example:
# Correct: super().__init__() # Wrong: super(ClassName, self).__init__()
New in version 0.7.0.
Changed in version 0.11.0.
- error_template: ClassVar[str] = 'Found incorrect `super()` call: {0}'¶
- code: ClassVar[int] = 608¶
- previous_codes: ClassVar[Set[int]] = {456}¶
- full_code: ClassVar[str] = 'WPS608'¶
- summary: ClassVar[str] = 'Forbid ``super()`` with parameters or outside of methods.'¶
- class DirectMagicAttributeAccessViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS609 — Forbid directly calling certain magic attributes and methods.
- Reasoning:
Certain magic methods are only meant to be called by particular functions or operators, not directly accessed.
- Solution:
Use special syntax constructs that will call underlying magic methods.
Example:
# Correct: super().__init__() mymodule.__name__ # Wrong: foo.__str__() # use `str(foo)` 2..__truediv__(2) # use `2 / 2` d.__delitem__('a') # use del d['a']
Note, that it is possible to directly use these magic attributes with
self,cls, andsuper()as base names. We allow this because a lot of internal logic relies on these methods.See
ALL_MAGIC_METHODSfor the full list of magic attributes disallowed from being accessed directly.New in version 0.8.0.
Changed in version 0.11.0.
Changed in version 0.16.0.
- error_template: ClassVar[str] = 'Found direct magic attribute usage: {0}'¶
- code: ClassVar[int] = 609¶
- previous_codes: ClassVar[Set[int]] = {462}¶
- full_code: ClassVar[str] = 'WPS609'¶
- summary: ClassVar[str] = 'Forbid directly calling certain magic attributes and methods.'¶
- class AsyncMagicMethodViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS610 — Forbid certain async magic methods.
We allow to make
__anext__,__aenter__,__aexit__async. We allow to make__aiter__async if it is a generator (contains yield). We also allow custom magic methods to be async.See
ASYNC_MAGIC_METHODS_BLACKLISTfor the whole list of blacklisted async magic methods.- Reasoning:
Defining the magic methods as async which are not supposed to be async would not work as expected.
- Solution:
Do not make this magic method async.
Example:
# Correct: class Test(object): def __lt__(self, other): ... # Wrong: class Test(object): async def __lt__(self, other): ...
New in version 0.12.0.
- error_template: ClassVar[str] = 'Found forbidden `async` magic method usage: {0}'¶
- code: ClassVar[int] = 610¶
- full_code: ClassVar[str] = 'WPS610'¶
- summary: ClassVar[str] = 'Forbid certain async magic methods.'¶
- class YieldMagicMethodViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS611 — Forbid
yieldinside of certain magic methods.We allow to make
__iter__a generator. We allow to make__aiter__an async generator. SeeYIELD_MAGIC_METHODS_BLACKLISTfor the whole list of blacklisted generator magic methods.- Reasoning:
Python’s datamodel is strict. You cannot make generators from random magic methods. This rule enforces it.
- Solution:
Remove
yieldfrom a magic method or rename it to be a custom method.
Example:
# Correct: class Example(object): def __init__(self): ... # Wrong: class Example(object): def __init__(self): yield 10
New in version 0.3.0.
Changed in version 0.11.0.
Changed in version 0.12.0.
- error_template: ClassVar[str] = 'Found forbidden `yield` magic method usage: {0}'¶
- code: ClassVar[int] = 611¶
- previous_codes: ClassVar[Set[int]] = {435, 439}¶
- full_code: ClassVar[str] = 'WPS611'¶
- summary: ClassVar[str] = 'Forbid ``yield`` inside of certain magic methods.'¶
- class UselessOverwrittenMethodViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS612 — Forbid useless overwritten methods.
- Reasoning:
Overwriting method without any changes does not have any positive impact.
- Solution:
Do not overwrite method in case you do not want to do any changes inside it.
Example:
# Correct: class Test(Base): def method(self, argument): super().method(argument) return argument # or None, or anything! # Wrong: class Test(object): def method(self, argument): return super().method(argument)
New in version 0.12.0.
- error_template: ClassVar[str] = 'Found useless overwritten method: {0}'¶
- code: ClassVar[int] = 612¶
- full_code: ClassVar[str] = 'WPS612'¶
- summary: ClassVar[str] = 'Forbid useless overwritten methods.'¶
- class WrongSuperCallAccessViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS613 — Forbid
super()with incorrect method or property access.- Reasoning:
Can only use
super()method that matches the following context.super().some()andsuper().someinChild.some(), andsuper().propandsuper().prop()inChild.prop- Solution:
Use
super()methods and properties with the correct context.
Example:
# Correct: class Child(Parent): def some_method(self): original = super().some_method() # Wrong: class Child(Parent): def some_method(self): other = super().other_method()
New in version 0.13.0.
- error_template: ClassVar[str] = 'Found incorrect `super()` call context: incorrect name access'¶
- code: ClassVar[int] = 613¶
- full_code: ClassVar[str] = 'WPS613'¶
- summary: ClassVar[str] = 'Forbid ``super()`` with incorrect method or property access.'¶
- class WrongDescriptorDecoratorViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS614 — Forbids descriptors in regular functions.
Forbids using @staticmethod,
@classmethodand@propertyfor functions not in class.- Reasoning:
Descriptors like @staticmethod, @classmethod and @property do magic only as methods. We would want to warn users if the descriptors are used on regular functions.
- Solution:
Do not use @staticmethod, @classmethod and @property on regular functions or wrap the functions into a Class.
Example:
# Correct: class TestClass(object): @property def my_method(): ... # Wrong: @property def my_function(): ...
New in version 0.15.0.
- error_template: ClassVar[str] = 'Found descriptor applied on a function'¶
- code: ClassVar[int] = 614¶
- full_code: ClassVar[str] = 'WPS614'¶
- summary: ClassVar[str] = 'Forbids descriptors in regular functions.'¶
- class UnpythonicGetterSetterViolation(node, text=None, baseline=None)[source]¶
Bases:
ASTViolationWPS615 — Forbids to use getters and setters in objects.
- Reasoning:
Python does not need this abstraction.
- Solution:
Either use
@propertyor make the attribute public and change it directly.
Example:
# Correct: class Example(object): def __init__(self): self.attribute = None # Wrong: class Example(object): def __init__(self): self._attribute = None def set_attribute(self, value): ... def get_attribute(self): ...
New in version 0.15.0.
- error_template: ClassVar[str] = 'Found unpythonic getter or setter'¶
- code: ClassVar[int] = 615¶
- full_code: ClassVar[str] = 'WPS615'¶
- summary: ClassVar[str] = 'Forbids to use getters and setters in objects.'¶