org.sonar.l10n.py.rules.python.S2638.html Maven / Gradle / Ivy
This rule raises an issue when an overriding method changes a contract defined in a superclass.
Why is this an issue?
Because a subclass instance may be used as an instance of the superclass, overriding methods should uphold the aspects of the superclass contract
that relate to the Liskov Substitution Principle. Specifically, an
overriding method should be callable with the same parameters as the overriden one.
The following modifications are OK:
- Adding an optional parameter, i.e. with a default value, as long as they don’t change the order of positional parameters.
- Renaming a positional-only parameter.
- Reordering keyword-only parameters.
- Adding a default value to an existing parameter.
- Changing the default value of an existing parameter.
- Extend the ways a parameter can be provided, i.e. change a keyword-only or positional-only parameter to a keyword-or-positional parameter. This
is only true if the order of positional parameters doesn’t change. New positional parameters should be placed at the end.
- Adding a vararg parameter (
*args
).
- Adding a keywords parameter (
**kwargs
).
The following modifications are not OK:
- Removing parameters, even when they have default values.
- Adding mandatory parameters, i.e. without a default value.
- Removing the default value of a parameter.
- Reordering parameters, except when they are keyword-only parameters.
- Removing some ways of providing a parameter. If a parameter could be passed as keyword it should still be possible to pass it as keyword, and
the same is true for positional parameters.
- Removing a vararg parameter (
*args
).
- Removing a keywords parameter (
**kwargs
).
This rule raises an issue when the signature of an overriding method does not accept the same parameters as the overriden one. Only instance
methods are considered, class methods and static methods are ignored.
Exceptions
In theory, renaming parameters also breaks Liskov Substitution Principle. Arguments can’t be passed via keyword arguments anymore. However, PEP-570 indicates it is common to rename parameters when it improves
code readability and when arguments are always passed by position.
"Positional-Only Parameters" were introduced in Python 3.8 to solve this problem. As most programs will need to support older versions of Python,
this rule won’t raise an issue on renamed parameters.
class ParentClass(object):
def mymethod(self, param1):
pass
class ChildClassRenamed(ParentClass):
def mymethod(self, renamed): # No issue but this is suspicious. Rename this parameter as "param1" or use positional only arguments if possible.
pass
Code examples
Noncompliant code example
class ParentClass(object):
def mymethod(self, param1):
pass
class ChildClassMore(ParentClass):
def mymethod(self, param1, param2, param3): # Noncompliant * 2.
# Remove parameter "param2" or provide a default value.
# Remove parameter "param3" or provide a default value.
pass
class ChildClassLess(ParentClass):
def mymethod(self): # Noncompliant. Add missing parameter "param1".
pass
class ChildClassReordered(ParentClass):
def mymethod(self, inserted, param1): # Noncompliant
# Remove parameters "inserted" or provide a default value.
pass
Compliant solution
class ParentClass(object):
def mymethod(self, param1):
pass
class ChildClassMore(ParentClass):
def mymethod(self, param1, param2=None, param3=None):
pass
class ChildClassLess(ParentClass):
def mymethod(self, param1=None):
pass
class ChildClassReordered(ParentClass):
def mymethod(self, param1, inserted=None):
pass
Resources
Documentation
- SOLID - Wikipedia - Liskov substitution principle
- Python Enhancement Proposal (PEP) 3102 - Keyword-Only Arguments
- Python Enhancement Proposal (PEP) 570 - Python Positional-Only Parameters