From d4b2e9224839aed8fc160ebe5a89e0f7d4c6a5bb Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 28 Aug 2020 14:11:21 +0000 Subject: [FIX] core: cursor hooks API and implementation Python 3.8 changed the equality rules for bound methods to be based on the *identity* of the receiver (`__self__`) rather than its *equality*. This means that in 3.7, methods from different instances will compare (and hash) equal, thereby landing in the same map "slot", but that isn't the case in 3.8. While it's usually not relevant, it's an issue for `GroupCalls` which is indexed by a function: in 3.7, that being a method from recordsets comparing equal will deduplicate them, but not anymore in 3.8, leading to duplicated callbacks (exactly the thing GroupCalls aims to avoid). Also, the API of `GroupCalls` turned out to be unusual and weird. The bug above is fixed by using a plain list for callbacks, thereby avoiding comparisons between registered functions. The API is now: callbacks.add(func) # add func to callbacks callbacks.run() # run all callbacks in addition order callbacks.clear() # remove all callbacks In order to handle aggregated data, the `callbacks` object provides a dictionary `callbacks.data` that any callback function can freely use. For the sake of consistency, the `callbacks.data` dict is automatically cleared upon execution of callbacks. Discovered by @william-andre Related to odoo#56583 References: * https://bugs.python.org/issue1617161 * python/cpython#7848 * https://docs.python.org/3/whatsnew/changelog.html#python-3-8-0-alpha-1 (no direct link because individual entries are not linkable, look for bpo-1617161) X-original-commit: a986f4929bab95759086cc77ca5e60795f82e91a --- addons/mail/models/mail_thread.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'addons/mail/models/mail_thread.py') diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 24adb15b450..802fef140ae 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -516,8 +516,8 @@ class MailThread(models.AbstractModel): fnames = self._get_tracked_fields().intersection(fields) if not fnames: return - func = self.browse()._finalize_tracking - [initial_values] = self.env.cr.precommit.add(func, dict) + self.env.cr.precommit.add(self._finalize_tracking) + initial_values = self.env.cr.precommit.data.setdefault(f'mail.tracking.{self._name}', {}) for record in self: if not record.id: continue @@ -530,16 +530,17 @@ class MailThread(models.AbstractModel): """ Prevent any tracking of fields on ``self``. """ if not self._get_tracked_fields(): return - func = self.browse()._finalize_tracking - [initial_values] = self.env.cr.precommit.add(func, dict) + self.env.cr.precommit.add(self._finalize_tracking) + initial_values = self.env.cr.precommit.data.setdefault(f'mail.tracking.{self._name}', {}) # disable tracking by setting initial values to None for id_ in self.ids: initial_values[id_] = None - def _finalize_tracking(self, initial_values): + def _finalize_tracking(self): """ Generate the tracking messages for the records that have been prepared with ``_prepare_tracking``. """ + initial_values = self.env.cr.precommit.data.pop(f'mail.tracking.{self._name}', {}) ids = [id_ for id_, vals in initial_values.items() if vals] if not ids: return -- cgit v1.2.3