Skip to content

OD229 override-signature-mismatch

An override refuses a call that the method it overrides takes.

Category Severity Series Needs the index
Correctness Error saas~12.5 and later yes

What it reports

A method of a class that extends its model in place, with _inherit and no other _name, whose parameters refuse a call that passes every parameter each definition it overrides requires:

  • a positional parameter the parent requires, past those the override takes by position, when the override has no *args;
  • a parameter the override requires that no call made for the parent passes: a positional one past the parent's, when the parent has no *args, or a keyword-only one the parent cannot take by name, when the parent has no **kwargs;
  • a keyword-only parameter of the parent that the override cannot take by name, when the override has no **kwargs, and the parent requires it or the override adds a keyword-only parameter in its place.

The definitions it overrides are those of the other classes the registry merges into the model: in the modules it depends on and those Odoo installs with base, short of one that depends on this module, and above the class in its own file. The module's other files are left out, as their order is that of the imports. Every one of them must refuse the call. The message names the one that loads first, its module and its parameters, and the parameter the call fails on.

An override that drops an optional parameter, makes a default required or renames a positional parameter is not reported, as core overrides do all three where no caller passes that parameter. Nor is an override in a class that defines a model, whose callers may be its own.

Why it matters

Odoo calls a method on the model's records, so a caller written for the parent reaches the override. When the override's parameters do not fit that call, Python raises TypeError and the action fails. The usual cause is a parameter added upstream between versions that the override does not take. Up to saas~12.4, Odoo wraps every method in api.guess, which adapts old-style calls, so the rule starts at saas~12.5.

The rule stays silent when a definition has a decorator that may change the arguments it takes: any but those of api, staticmethod, classmethod, override, ormcache and ormcache_context. It is also silent when a class binds the method's name other than with a def in its body, such as under an if. Python renames a __name method per class name, so it is compared only with classes of the same name, leading underscores aside. The rule stays silent where OD211 does, and when a class of the model or of a model it inherits has a plain Python class among its bases, whose methods the scan cannot read. A base its file defines above that class with nothing but methods and literals in its body is read: an override of a name it binds and that class does not define is not compared, as the scan keeps no signature of the base's, and every other override is.

Example

models/stock_rule.py
class StockRule(models.Model):
    _inherit = "stock.rule"

    def _get_matching_bom(self, product_id, company_id):
        return super()._get_matching_bom(product_id, company_id, {})

mrp defines _get_matching_bom with a last parameter, values, which _run_manufacture passes.

models/stock_rule.py
class StockRule(models.Model):
    _inherit = "stock.rule"

    def _get_matching_bom(self, product_id, company_id, values):
        return super()._get_matching_bom(product_id, company_id, values)

Suppressing

def _get_matching_bom(self, product_id, company_id):  # odooscan: ignore[OD229]

See suppressing a finding.