[FIX] addons: fix usage of decorators `api.v7`/`api.v8`

Specifically, when one API implementation calls the other one, it has to call
the method *from the same class*.  Otherwise, overriding the method may result
in an infinite recursion.  Consider:

    class A(Model):
        _name = 'stuff'

        @api.v8
        def foo(self):
            return 42

        @api.v7
        def foo(self, cr, uid, context=None):
            return self.browse(cr, uid, [], context).foo()

    class B(Model):
        _inherit = 'stuff'

        def foo(self, cr, uid, context=None):
            return super(B, self).foo(cr, uid, context=context) + 1

and now call: `env['stuff'].foo()`.  This invokes `B.foo` (new-API), which
calls `B.foo` (old-API), which calls `A.foo` (old-API), which calls `B.foo`
(new-API) instead of `A.foo`!

This issue would not be present if old-API `A.foo` was defined as:

        @api.v7
        def foo(self, cr, uid, context=None):
            return A.foo(self.browse(cr, uid, [], context))
This commit is contained in:
Raphael Collet 2016-02-09 10:01:34 +01:00
parent d82e489add
commit 5763c32a6d
4 changed files with 22 additions and 12 deletions

View File

@ -2141,8 +2141,8 @@ class account_tax(osv.osv):
@api.v8
def compute_all(self, price_unit, quantity, product=None, partner=None, force_excluded=False):
return self._model.compute_all(
self._cr, self._uid, self, price_unit, quantity,
return account_tax.compute_all(
self._model, self._cr, self._uid, self, price_unit, quantity,
product=product, partner=partner, force_excluded=force_excluded)
def compute(self, cr, uid, taxes, price_unit, quantity, product=None, partner=None):

View File

@ -1209,7 +1209,7 @@ class account_invoice(models.Model):
def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id,
writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''):
recs = self.browse(cr, uid, ids, context)
return recs.pay_and_reconcile(pay_amount, pay_account_id, period_id, pay_journal_id,
return account_invoice.pay_and_reconcile(recs, pay_amount, pay_account_id, period_id, pay_journal_id,
writeoff_acc_id, writeoff_period_id, writeoff_journal_id, name=name)
class account_invoice_line(models.Model):
@ -1600,7 +1600,7 @@ class account_invoice_tax(models.Model):
def compute(self, cr, uid, invoice_id, context=None):
recs = self.browse(cr, uid, [], context)
invoice = recs.env['account.invoice'].browse(invoice_id)
return recs.compute(invoice)
return account_invoice_tax.compute(recs, invoice)
@api.model
def move_line_get(self, invoice_id):

View File

@ -177,8 +177,8 @@ class Report(osv.Model):
@api.v8
def get_html(self, records, report_name, data=None):
return self._model.get_html(self._cr, self._uid, records.ids, report_name,
data=data, context=self._context)
return Report.get_html(self._model, self._cr, self._uid, records.ids,
report_name, data=data, context=self._context)
@api.v7
def get_pdf(self, cr, uid, ids, report_name, html=None, data=None, context=None):
@ -276,8 +276,8 @@ class Report(osv.Model):
@api.v8
def get_pdf(self, records, report_name, html=None, data=None):
return self._model.get_pdf(self._cr, self._uid, records.ids, report_name,
html=html, data=data, context=self._context)
return Report.get_pdf(self._model, self._cr, self._uid, records.ids,
report_name, html=html, data=data, context=self._context)
@api.v7
def get_action(self, cr, uid, ids, report_name, data=None, context=None):
@ -313,8 +313,8 @@ class Report(osv.Model):
@api.v8
def get_action(self, records, report_name, data=None):
return self._model.get_action(self._cr, self._uid, records.ids, report_name,
data=data, context=self._context)
return Report.get_action(self._model, self._cr, self._uid, records.ids,
report_name, data=data, context=self._context)
#--------------------------------------------------------------------------
# Report generation helpers
@ -360,8 +360,8 @@ class Report(osv.Model):
@api.v8
def _check_attachment_use(self, records, report):
return self._model._check_attachment_use(
self._cr, self._uid, records.ids, report, context=self._context)
return Report._check_attachment_use(
self._model, self._cr, self._uid, records.ids, report, context=self._context)
def _check_wkhtmltopdf(self):
return wkhtmltopdf_state

View File

@ -608,6 +608,16 @@ def v7(method_v7):
def foo(self):
...
Special care must be taken if one method calls the other one, because
the method may be overridden! In that case, one should call the method
from the current class (say ``MyClass``), for instance::
@api.v7
def foo(self, cr, uid, ids, context=None):
# Beware: records.foo() may call an overriding of foo()
records = self.browse(cr, uid, ids, context)
return MyClass.foo(records)
Note that the wrapper method uses the docstring of the first method.
"""
# retrieve method_v8 from the caller's frame