[FIX] api: fix api decorators to not add '_api' on original methods

This fixes an issue where the same function is used in several model classes:
once the function is wrapped for the first class, it is erroneously considered
as a wrapper for the second class, and is therefore not wrapped in other
classes.
This commit is contained in:
Raphael Collet 2015-01-15 15:01:26 +01:00
parent 48002b3278
commit 309e3def30
1 changed files with 26 additions and 32 deletions

View File

@ -73,7 +73,6 @@ from openerp.tools import frozendict
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
# The following attributes are used, and reflected on wrapping methods: # The following attributes are used, and reflected on wrapping methods:
# - method._api: decorator function, used for re-applying decorator
# - method._constrains: set by @constrains, specifies constraint dependencies # - method._constrains: set by @constrains, specifies constraint dependencies
# - method._depends: set by @depends, specifies compute dependencies # - method._depends: set by @depends, specifies compute dependencies
# - method._returns: set by @returns, specifies return model # - method._returns: set by @returns, specifies return model
@ -81,10 +80,11 @@ _logger = logging.getLogger(__name__)
# - method.clear_cache: set by @ormcache, used to clear the cache # - method.clear_cache: set by @ormcache, used to clear the cache
# #
# On wrapping method only: # On wrapping method only:
# - method._api: decorator function, used for re-applying decorator
# - method._orig: original method # - method._orig: original method
# #
WRAPPED_ATTRS = ('__module__', '__name__', '__doc__', '_api', '_constrains', WRAPPED_ATTRS = ('__module__', '__name__', '__doc__', '_constrains',
'_depends', '_onchange', '_returns', 'clear_cache') '_depends', '_onchange', '_returns', 'clear_cache')
INHERITED_ATTRS = ('_returns',) INHERITED_ATTRS = ('_returns',)
@ -121,10 +121,14 @@ identity = lambda x: x
def decorate(method, attr, value): def decorate(method, attr, value):
""" Decorate `method` or its original method. """ """ Decorate `method` or its original method. """
# decorate the original method, and re-apply the api decorator, if any if getattr(method, '_api', False):
orig = getattr(method, '_orig', method) # decorate the original method, and re-apply the api decorator
setattr(orig, attr, value) setattr(method._orig, attr, value)
return getattr(method, '_api', identity)(orig) return method._api(method._orig)
else:
# simply decorate the method itself
setattr(method, attr, value)
return method
def propagate(from_method, to_method): def propagate(from_method, to_method):
""" Propagate decorators from `from_method` to `to_method`, and return the """ Propagate decorators from `from_method` to `to_method`, and return the
@ -227,7 +231,7 @@ def returns(model, downgrade=None):
return lambda method: decorate(method, '_returns', (model, downgrade)) return lambda method: decorate(method, '_returns', (model, downgrade))
def make_wrapper(method, old_api, new_api): def make_wrapper(decorator, method, old_api, new_api):
""" Return a wrapper method for `method`. """ """ Return a wrapper method for `method`. """
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
# avoid hasattr(self, '_ids') because __getattr__() is overridden # avoid hasattr(self, '_ids') because __getattr__() is overridden
@ -240,6 +244,7 @@ def make_wrapper(method, old_api, new_api):
for attr in WRAPPED_ATTRS: for attr in WRAPPED_ATTRS:
if hasattr(method, attr): if hasattr(method, attr):
setattr(wrapper, attr, getattr(method, attr)) setattr(wrapper, attr, getattr(method, attr))
wrapper._api = decorator
wrapper._orig = method wrapper._orig = method
return wrapper return wrapper
@ -322,7 +327,6 @@ def model(method):
Notice that no `ids` are passed to the method in the traditional style. Notice that no `ids` are passed to the method in the traditional style.
""" """
method._api = model
split = get_context_split(method) split = get_context_split(method)
downgrade = get_downgrade(method) downgrade = get_downgrade(method)
@ -332,7 +336,7 @@ def model(method):
result = method(recs, *args, **kwargs) result = method(recs, *args, **kwargs)
return downgrade(result) return downgrade(result)
return make_wrapper(method, old_api, method) return make_wrapper(model, method, old_api, method)
def multi(method): def multi(method):
@ -350,7 +354,6 @@ def multi(method):
model.method(cr, uid, ids, args, context=context) model.method(cr, uid, ids, args, context=context)
""" """
method._api = multi
split = get_context_split(method) split = get_context_split(method)
downgrade = get_downgrade(method) downgrade = get_downgrade(method)
@ -360,7 +363,7 @@ def multi(method):
result = method(recs, *args, **kwargs) result = method(recs, *args, **kwargs)
return downgrade(result) return downgrade(result)
return make_wrapper(method, old_api, method) return make_wrapper(multi, method, old_api, method)
def one(method): def one(method):
@ -380,7 +383,6 @@ def one(method):
names = model.method(cr, uid, ids, args, context=context) names = model.method(cr, uid, ids, args, context=context)
""" """
method._api = one
split = get_context_split(method) split = get_context_split(method)
downgrade = get_downgrade(method) downgrade = get_downgrade(method)
aggregate = get_aggregate(method) aggregate = get_aggregate(method)
@ -395,7 +397,7 @@ def one(method):
result = [method(rec, *args, **kwargs) for rec in self] result = [method(rec, *args, **kwargs) for rec in self]
return aggregate(self, result) return aggregate(self, result)
return make_wrapper(method, old_api, new_api) return make_wrapper(one, method, old_api, new_api)
def cr(method): def cr(method):
@ -407,7 +409,6 @@ def cr(method):
model.method(cr, args) model.method(cr, args)
""" """
method._api = cr
upgrade = get_upgrade(method) upgrade = get_upgrade(method)
def new_api(self, *args, **kwargs): def new_api(self, *args, **kwargs):
@ -415,12 +416,11 @@ def cr(method):
result = method(self._model, cr, *args, **kwargs) result = method(self._model, cr, *args, **kwargs)
return upgrade(self, result) return upgrade(self, result)
return make_wrapper(method, method, new_api) return make_wrapper(cr, method, method, new_api)
def cr_context(method): def cr_context(method):
""" Decorate a traditional-style method that takes `cr`, `context` as parameters. """ """ Decorate a traditional-style method that takes `cr`, `context` as parameters. """
method._api = cr_context
upgrade = get_upgrade(method) upgrade = get_upgrade(method)
def new_api(self, *args, **kwargs): def new_api(self, *args, **kwargs):
@ -429,12 +429,11 @@ def cr_context(method):
result = method(self._model, cr, *args, **kwargs) result = method(self._model, cr, *args, **kwargs)
return upgrade(self, result) return upgrade(self, result)
return make_wrapper(method, method, new_api) return make_wrapper(cr_context, method, method, new_api)
def cr_uid(method): def cr_uid(method):
""" Decorate a traditional-style method that takes `cr`, `uid` as parameters. """ """ Decorate a traditional-style method that takes `cr`, `uid` as parameters. """
method._api = cr_uid
upgrade = get_upgrade(method) upgrade = get_upgrade(method)
def new_api(self, *args, **kwargs): def new_api(self, *args, **kwargs):
@ -442,7 +441,7 @@ def cr_uid(method):
result = method(self._model, cr, uid, *args, **kwargs) result = method(self._model, cr, uid, *args, **kwargs)
return upgrade(self, result) return upgrade(self, result)
return make_wrapper(method, method, new_api) return make_wrapper(cr_uid, method, method, new_api)
def cr_uid_context(method): def cr_uid_context(method):
@ -455,7 +454,6 @@ def cr_uid_context(method):
model.method(cr, uid, args, context=context) model.method(cr, uid, args, context=context)
""" """
method._api = cr_uid_context
upgrade = get_upgrade(method) upgrade = get_upgrade(method)
def new_api(self, *args, **kwargs): def new_api(self, *args, **kwargs):
@ -464,7 +462,7 @@ def cr_uid_context(method):
result = method(self._model, cr, uid, *args, **kwargs) result = method(self._model, cr, uid, *args, **kwargs)
return upgrade(self, result) return upgrade(self, result)
return make_wrapper(method, method, new_api) return make_wrapper(cr_uid_context, method, method, new_api)
def cr_uid_id(method): def cr_uid_id(method):
@ -472,7 +470,6 @@ def cr_uid_id(method):
parameters. Such a method may be called in both record and traditional parameters. Such a method may be called in both record and traditional
styles. In the record style, the method automatically loops on records. styles. In the record style, the method automatically loops on records.
""" """
method._api = cr_uid_id
upgrade = get_upgrade(method) upgrade = get_upgrade(method)
def new_api(self, *args, **kwargs): def new_api(self, *args, **kwargs):
@ -480,7 +477,7 @@ def cr_uid_id(method):
result = [method(self._model, cr, uid, id, *args, **kwargs) for id in self.ids] result = [method(self._model, cr, uid, id, *args, **kwargs) for id in self.ids]
return upgrade(self, result) return upgrade(self, result)
return make_wrapper(method, method, new_api) return make_wrapper(cr_uid_id, method, method, new_api)
def cr_uid_id_context(method): def cr_uid_id_context(method):
@ -498,7 +495,6 @@ def cr_uid_id_context(method):
model.method(cr, uid, id, args, context=context) model.method(cr, uid, id, args, context=context)
""" """
method._api = cr_uid_id_context
upgrade = get_upgrade(method) upgrade = get_upgrade(method)
def new_api(self, *args, **kwargs): def new_api(self, *args, **kwargs):
@ -507,7 +503,7 @@ def cr_uid_id_context(method):
result = [method(self._model, cr, uid, id, *args, **kwargs) for id in self.ids] result = [method(self._model, cr, uid, id, *args, **kwargs) for id in self.ids]
return upgrade(self, result) return upgrade(self, result)
return make_wrapper(method, method, new_api) return make_wrapper(cr_uid_id_context, method, method, new_api)
def cr_uid_ids(method): def cr_uid_ids(method):
@ -515,7 +511,6 @@ def cr_uid_ids(method):
parameters. Such a method may be called in both record and traditional parameters. Such a method may be called in both record and traditional
styles. styles.
""" """
method._api = cr_uid_ids
upgrade = get_upgrade(method) upgrade = get_upgrade(method)
def new_api(self, *args, **kwargs): def new_api(self, *args, **kwargs):
@ -523,7 +518,7 @@ def cr_uid_ids(method):
result = method(self._model, cr, uid, self.ids, *args, **kwargs) result = method(self._model, cr, uid, self.ids, *args, **kwargs)
return upgrade(self, result) return upgrade(self, result)
return make_wrapper(method, method, new_api) return make_wrapper(cr_uid_ids, method, method, new_api)
def cr_uid_ids_context(method): def cr_uid_ids_context(method):
@ -543,7 +538,6 @@ def cr_uid_ids_context(method):
It is generally not necessary, see :func:`guess`. It is generally not necessary, see :func:`guess`.
""" """
method._api = cr_uid_ids_context
upgrade = get_upgrade(method) upgrade = get_upgrade(method)
def new_api(self, *args, **kwargs): def new_api(self, *args, **kwargs):
@ -552,7 +546,7 @@ def cr_uid_ids_context(method):
result = method(self._model, cr, uid, self.ids, *args, **kwargs) result = method(self._model, cr, uid, self.ids, *args, **kwargs)
return upgrade(self, result) return upgrade(self, result)
return make_wrapper(method, method, new_api) return make_wrapper(cr_uid_ids_context, method, method, new_api)
def v7(method_v7): def v7(method_v7):
@ -575,7 +569,7 @@ def v7(method_v7):
method = frame.f_locals.get(method_v7.__name__) method = frame.f_locals.get(method_v7.__name__)
method_v8 = getattr(method, '_v8', method) method_v8 = getattr(method, '_v8', method)
wrapper = make_wrapper(method_v7, method_v7, method_v8) wrapper = make_wrapper(v7, method_v7, method_v7, method_v8)
wrapper._v7 = method_v7 wrapper._v7 = method_v7
wrapper._v8 = method_v8 wrapper._v8 = method_v8
return wrapper return wrapper
@ -601,7 +595,7 @@ def v8(method_v8):
method = frame.f_locals.get(method_v8.__name__) method = frame.f_locals.get(method_v8.__name__)
method_v7 = getattr(method, '_v7', method) method_v7 = getattr(method, '_v7', method)
wrapper = make_wrapper(method_v8, method_v7, method_v8) wrapper = make_wrapper(v8, method_v8, method_v7, method_v8)
wrapper._v7 = method_v7 wrapper._v7 = method_v7
wrapper._v8 = method_v8 wrapper._v8 = method_v8
return wrapper return wrapper
@ -664,7 +658,7 @@ def guess(method):
def expected(decorator, func): def expected(decorator, func):
""" Decorate `func` with `decorator` if `func` is not wrapped yet. """ """ Decorate `func` with `decorator` if `func` is not wrapped yet. """
return decorator(func) if not hasattr(func, '_orig') else func return decorator(func) if not hasattr(func, '_api') else func