OD319 python-field-syntax-error¶
The code of a server action or a cron, or the file name of a report, is not Python that Odoo compiles.
| Category | Severity | Series | Needs the index |
|---|---|---|---|
| Correctness | Error | all | no |
What it reports¶
The code field of an ir.actions.server or ir.cron record in a data file the manifest loads
that Python 3 does not compile. That covers a syntax error, and also what only the compiler refuses:
return, yield or await outside a function, and break or continue outside a loop. Every
series is read as Python 3, so code only Python 2 compiles, such as x = 0644, is reported on 9.0
and 10.0, where it installs.
ir.cron has code from 11.0, through its delegated server action (11.0
odoo/addons/base/ir/ir_cron.py:50-52). Below that a cron names a model and a function (10.0
odoo/addons/base/ir/ir_cron.py:67-68), so Odoo logs a warning and drops a code field (10.0
odoo/models.py:3876), and nothing compiles or runs it. The rule stays silent there, and
OD309 reports the field when base is indexed.
async and await are reserved words from 15.0, whose floor is Python 3.7, so code or a report
file name using either as a name fails there: rename it. Below 15.0, and in a module of unknown
series, the rule reads them as names outside an async def block, even on an install running a
newer Python.
Odoo strips the text before it compiles it, which only touches the first line. In an indented block, every line after the first keeps its indentation, so Python refuses it as an unexpected indent.
It also reports the print_report_name of an ir.actions.report that does not compile as one
expression, written by a <record> from 11.0 on or by a <report> tag from 11.0 to 16.0, whatever
its report_type. A statement such as name = 1, a second line outside brackets, and text of only
whitespace all count. An empty value counts as unset.
The finding sits on the line and column of the error in the source. In a <report> attribute the
column counts decoded bytes from the start of the value, and the line is the attribute's. Past
an entity declared in a DOCTYPE it sits on that entity, and a text that opens with one has no
reliable position.
Why it matters¶
From 10.0 a constraint compiles code when the record is written, in any state (10.0
odoo/addons/base/ir/ir_actions.py:608-609), so a broken one raises a ValidationError and stops
the install or the upgrade of the module. A demo file differs: see load
failures. Each write to the record
counts, even one that a later module replaces.
Below 10.0 the only constraints are on write_expression and child_ids (9.0
openerp/addons/base/ir/ir_actions.py:706-712), so the install passes. Broken code fails when an
action whose state is code runs (9.0 openerp/addons/base/ir/ir_actions.py:918-919,1086). The
rule reports it only where the record still holds that code and runs it, under the conditions of
OD320.
A module of unknown series may be on either side of the constraint, so each write reports, and the message names both failures.
The rule reads the text of the field and any CDATA, up to the first child element or comment, and
the last code field of a record. It leaves alone a field with an eval, ref, search or file
that is not empty, a field with a type, blank code, and CSV files. It checks neither the opcodes
nor the names safe_eval refuses.
A file name fails later: safe_eval compiles it when the report prints, so the install passes and
the error reaches whoever prints the report or sends a mail template that attaches it. Only the
value the record still holds reports: the last field of a name, across the writes of the module and
its dependencies, an eval or an empty field included. A demo write does not count for a data
value, as a database may hold no demo data. A <report> tag stays silent in a module of unknown
series.
Example¶
<odoo>
<record id="cron_close_orders" model="ir.cron">
<field name="name">Close orders</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="code">
model._close_orders()
model._notify_closed()
</field>
</record>
</odoo>
The second line keeps its indentation, so from 11.0 the module fails to install.
<odoo>
<record id="cron_close_orders" model="ir.cron">
<field name="name">Close orders</field>
<field name="model_id" ref="sale.model_sale_order"/>
<field name="code">
model._close_orders()
model._notify_closed()
</field>
</record>
</odoo>
A file name is one expression, so it takes no assignment:
<record id="action_report_invoice" model="ir.actions.report">
<field name="name">Invoice</field>
<field name="model">res.partner</field>
<field name="report_name">sale_portal.report_invoice</field>
<field name="print_report_name">name = 'Invoice - %s' % object.name</field>
</record>
<record id="action_report_invoice" model="ir.actions.report">
<field name="name">Invoice</field>
<field name="model">res.partner</field>
<field name="report_name">sale_portal.report_invoice</field>
<field name="print_report_name">'Invoice - %s' % object.name</field>
</record>
Suppressing¶
An XML comment inside the field ends the text Odoo reads, so inside the code use a Python comment,
# odooscan: ignore[OD319].