Skip to content

removed-odoo-method-call (ODE9503)

Preview (since 0.16.3.34) · Related issues · View source

Derived from the odoo linter.

This rule is unstable and in preview. The --preview flag is required for use.

What it does

Checks for calls to ORM model methods that no longer exist in the configured odoo-version.

Why is this bad?

Odoo deletes model methods between releases, most of them without a deprecation cycle: name_get, user_has_groups and copy_multi went in 18.0, _where_calc, clear_caches and _apply_ir_rules in 19.0. The call sites keep reading as valid Python and raise AttributeError the first time the line runs, which on a portal controller or a report means the branch nobody exercised during the migration.

Nothing else finds these. deprecated-odoo-method-call only knows the handful of methods Odoo marked @api.deprecated, and invalid-odoo-method-call binds arguments against a signature, so it goes quiet on exactly the methods that no longer have one.

The removal set is generated from Odoo's own source by scripts/generate_odoo_model_stubs.py, which subtracts every name Odoo still defines on some class before calling a method gone. That is what keeps two shapes out of it: a method that moved, like _condition_to_sql leaving BaseModel for Field in 19.0, and a name too ordinary to judge from the name alone, like refresh, which the ORM dropped in 17.0 and a hardware driver's browser still answers to.

The check needs a removal set to look in, so it reports nothing unless odoo-version is set to a version this linter ships one for. Since that silence is indistinguishable from a clean run, it warns once on stderr when the setting is missing or names a version with no set; the run still succeeds, and the warning only appears when this rule is enabled.

Scope

Any receiver counts, inside any class that inherits something which is not a Python builtin. Unlike the argument-binding rules there is no need to prove the receiver is a recordset: these names no longer exist anywhere in Odoo, so a call to one is either an ORM call that breaks or a name the project defined itself, and the second is checked for. That is what reaches the shape the migrations actually leave behind, a model looked up into a local:

sale_obj = request.env["sale.order"]
query = sale_obj._where_calc(domain)

A class that defines the method itself keeps its call, since the call means that definition rather than Odoo's. A class inheriting only object, Exception or another builtin is left alone: it is Python, not Odoo.

Example

class SaleOrder(models.Model):
    _inherit = "sale.order"

    def matching(self, domain):
        return self._where_calc(domain)

Use instead:

class SaleOrder(models.Model):
    _inherit = "sale.order"

    def matching(self, domain):
        return self._search(domain)