From 8ec6ae7d05edb381cc11deaa606f8067b2e9916f Mon Sep 17 00:00:00 2001 From: Kitti U Date: Sun, 27 Sep 2020 12:13:43 +0700 Subject: [PATCH] [13.0][IMP] queue_job, add decorator job_auto_delay Co-authored-by: Guewen Baconnier --- queue_job/job.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/queue_job/job.py b/queue_job/job.py index a7b7912981..67ffd16fde 100644 --- a/queue_job/job.py +++ b/queue_job/job.py @@ -899,3 +899,75 @@ def decorate(func): return func return decorate + + +def job_auto_delay(func=None, default_channel="root", retry_pattern=None): + """Decorator to automatically delay as job method when called + + The decorator applies ``odoo.addons.queue_job.job`` at the same time, + so the decorated method is listed in job functions. The arguments + are the same, propagated to the ``job`` decorator. + + When a method is decorated by ``job_auto_delay``, any call to the method + will not directly execute the method's body, but will instead enqueue a + job. + + The options of the job usually passed to ``with_delay()`` (priority, + description, identity_key, ...) can be returned in a dictionary by a method + named after the name of the method suffixed by ``_job_options`` which takes + the same parameters as the initial method. + + It is still possible to directly execute the method by setting a key + ``_job_force_sync`` to True in the environment context. + + Example: + + .. code-block:: python + + class ProductProduct(models.Model): + _inherit = 'product.product' + + def foo_job_options(self, arg1): + return { + "priority": 100, + "description": "Saying hello to {}".format(arg1) + } + + @job_auto_delay(default_channel="root.channel1") + def foo(self, arg1): + print("hello", arg1) + + def button_x(self): + foo("world") + + The result when ``button_x`` is called, is that a new job for ``foo`` is + delayed. + + """ + if func is None: + return functools.partial( + job_auto_delay, default_channel=default_channel, retry_pattern=retry_pattern + ) + + def auto_delay(self, *args, **kwargs): + if self.env.context.get("job_uuid") or self.env.context.get("_job_force_sync"): + # we are in the job execution + return func(self, *args, **kwargs) + else: + # replace the synchronous call by a job on itself + method_name = func.__name__ + job_options_method = getattr( + self, "{}_job_options".format(method_name), None + ) + job_options = {} + if job_options_method: + job_options.update(job_options_method(*args, **kwargs)) + else: + job_options = {} + delayed = self.with_delay(**job_options) + getattr(delayed, method_name)(*args, **kwargs) + + return functools.update_wrapper( + auto_delay, + job(func, default_channel=default_channel, retry_pattern=retry_pattern), + )