[REM] base: remove _patch_method and _revert_method

There is only one legitimate usage of `_patch_method` (base_automation)
and none of `_revert_method`. The other uses are in tests and they are
all wrong: if the test crashes between the `_patch_method` and the
`_revert_method`, the method will not be reverted.

For the only proper usage of `_patch_method`, move the code to this
place. Correct tests using `_patch_method`/`_revert_method` by calling
the `patch` method of `BaseCase`.

Also, remove the `api.returns` from the `create` method of `BaseModel`
as it is useless and confusing. In fact, we never use it because we
have a special treatment at the RPC level for the `create` method
(see `_call_kw_model_create`).

closes odoo/odoo#110370

Signed-off-by: Raphael Collet <rco@odoo.com>
This commit is contained in:
Rémy Voet (ryv)
2023-01-25 16:36:53 +01:00
committed by Raphael Collet
parent 01c2146d13
commit 5a870462e0
5 changed files with 12 additions and 59 deletions
@@ -498,7 +498,9 @@ class BaseAutomation(models.Model):
""" Patch method `name` on `model`, unless it has been patched already. """
if model not in patched_models[name]:
patched_models[name].add(model)
model._patch_method(name, method)
ModelClass = type(model)
method.origin = getattr(ModelClass, name)
setattr(ModelClass, name, method)
# retrieve all actions, and patch their corresponding model
for action_rule in self.with_context({}).search([]):
+5 -15
View File
@@ -833,7 +833,7 @@ class TestVariantsArchive(ProductVariantsCommon):
def unlink(self):
raise Exception('just')
Product._patch_method('unlink', unlink)
self.patch(type(Product), 'unlink', unlink)
variants_2x1 = self.template.product_variant_ids
self._assert_2color_x_1size()
@@ -855,8 +855,6 @@ class TestVariantsArchive(ProductVariantsCommon):
archived_variants = self._get_archived_variants()
self.assertFalse(archived_variants)
Product._revert_method('unlink')
def test_02_update_variant_archive_2_value(self):
"""We do the same operations on the template as in the previous tests,
except we simulate that the variants can't be unlinked.
@@ -868,7 +866,7 @@ class TestVariantsArchive(ProductVariantsCommon):
def unlink(slef):
raise Exception('just')
Product._patch_method('unlink', unlink)
self.patch(type(Product), 'unlink', unlink)
variants_2x2 = self.template.product_variant_ids
self._assert_2color_x_2size()
@@ -924,8 +922,6 @@ class TestVariantsArchive(ProductVariantsCommon):
self.assertEqual(archived_variants, variants_2x2)
self._assert_2color_x_2size(archived_variants)
Product._revert_method('unlink')
@mute_logger('odoo.models.unlink')
def test_03_update_variant_archive_3_value(self):
self._remove_ptal_size()
@@ -935,7 +931,7 @@ class TestVariantsArchive(ProductVariantsCommon):
def unlink(slef):
raise Exception('just')
Product._patch_method('unlink', unlink)
self.patch(type(Product), 'unlink', unlink)
self._assert_2color_x_1size()
archived_variants = self._get_archived_variants()
@@ -983,14 +979,12 @@ class TestVariantsArchive(ProductVariantsCommon):
archived_variants = self._get_archived_variants()
self.assertEqual(archived_variants, variants_2x1 + variant_0x0)
Product._revert_method('unlink')
def test_04_from_to_single_values(self):
Product = self.env['product.product']
def unlink(slef):
raise Exception('just')
Product._patch_method('unlink', unlink)
self.patch(type(Product), 'unlink', unlink)
# CASE: remove one value, line becoming single value
variants_2x2 = self.template.product_variant_ids
@@ -1027,8 +1021,6 @@ class TestVariantsArchive(ProductVariantsCommon):
self._assert_2color_x_0size(archived_variants)
self.assertEqual(archived_variants, variants_2x0)
Product._revert_method('unlink')
def test_name_search_dynamic_attributes(self):
dynamic_attr = self.env['product.attribute'].create({
'name': 'Dynamic',
@@ -1079,7 +1071,7 @@ class TestVariantsArchive(ProductVariantsCommon):
# Patch unlink method to force archiving instead deleting
def unlink(self):
self.active = False
Product._patch_method('unlink', unlink)
self.patch(type(Product), 'unlink', unlink)
# Creating attributes
pa_color = ProductAttribute.create({'sequence': 1, 'name': 'color', 'create_variant': 'dynamic'})
@@ -1184,8 +1176,6 @@ class TestVariantsArchive(ProductVariantsCommon):
})
self.assertTrue(product_white.active)
Product._revert_method('unlink')
def test_set_barcode(self):
tmpl = self.product.product_tmpl_id
tmpl.barcode = '123'
+2 -2
View File
@@ -779,7 +779,8 @@ class TestMailMailRace(common.TransactionCase):
# In practice, the update will wait the end of the send() transaction and set the notif as bounce, as expeced
cr.execute("UPDATE mail_notification SET notification_status='bounce' WHERE id = %s", [notif.id])
return message['Message-Id']
self.env['ir.mail_server']._patch_method('send_email', send_email)
self.patch(self.registry['ir.mail_server'], 'send_email', send_email)
mail.send()
@@ -787,7 +788,6 @@ class TestMailMailRace(common.TransactionCase):
self.assertEqual(notif.notification_status, 'sent')
# some cleaning since we commited the cr
self.env['ir.mail_server']._revert_method('send_email')
notif.unlink()
mail.unlink()
+2 -2
View File
@@ -222,14 +222,14 @@ class TestTracking(TestMailCommon):
if 'name' in init_values and init_values['name'] == magic_code:
return 'mail.mt_name_changed'
return False
self.registry('mail.test.container')._patch_method('_track_subtype', _track_subtype)
self.patch(self.registry('mail.test.container'), '_track_subtype', _track_subtype)
def _track_template(self, changes):
res = {}
if 'name' in changes:
res['name'] = (mail_template, {'composition_mode': 'mass_mail'})
return res
self.registry('mail.test.container')._patch_method('_track_template', _track_template)
self.patch(self.registry('mail.test.container'), '_track_template', _track_template)
cls = type(self.env['mail.test.container'])
self.assertFalse(hasattr(getattr(cls, 'name'), 'track_visibility'))
-39
View File
@@ -3827,7 +3827,6 @@ class BaseModel(metaclass=MetaModel):
parent_records._parent_store_update()
@api.model_create_multi
@api.returns('self', lambda value: value.id)
def create(self, vals_list):
""" create(vals_list) -> records
@@ -5008,44 +5007,6 @@ class BaseModel(metaclass=MetaModel):
def _unregister_hook(self):
""" Clean up what `~._register_hook` has done. """
@classmethod
def _patch_method(cls, name, method):
""" Monkey-patch a method for all instances of this model. This replaces
the method called ``name`` by ``method`` in the given class.
The original method is then accessible via ``method.origin``, and it
can be restored with :meth:`~._revert_method`.
Example::
def do_write(self, values):
# do stuff, and call the original method
return do_write.origin(self, values)
# patch method write of model
model._patch_method('write', do_write)
# this will call do_write
records = model.search([...])
records.write(...)
# restore the original method
model._revert_method('write')
"""
origin = getattr(cls, name)
method.origin = origin
# propagate decorators from origin to method, and apply api decorator
wrapped = api.propagate(origin, method)
wrapped.origin = origin
setattr(cls, name, wrapped)
@classmethod
def _revert_method(cls, name):
""" Revert the original method called ``name`` in the given class.
See :meth:`~._patch_method`.
"""
method = getattr(cls, name)
setattr(cls, name, method.origin)
#
# Instance creation
#