Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ exclude: |
# NOT INSTALLABLE ADDONS
^base_export_async/|
^base_import_async/|
^queue_job/|
^queue_job_cron/|
^queue_job_subscribe/|
^test_base_import_async/|
^test_queue_job/|
# END NOT INSTALLABLE ADDONS
# Files and folders generated by bots, to avoid loops
^setup/|/static/description/index\.html$|
Expand Down
6 changes: 3 additions & 3 deletions queue_job/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

{
"name": "Job Queue",
"version": "13.0.3.2.0",
"version": "14.0.1.0.0",
"author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/queue/queue_job",
"website": "https://github.com/OCA/queue",
"license": "LGPL-3",
"category": "Generic Modules",
"depends": ["mail"],
Expand All @@ -17,7 +17,7 @@
"data/queue_data.xml",
"data/queue_job_function_data.xml",
],
"installable": False,
"installable": True,
"development_status": "Mature",
"maintainers": ["guewen"],
"post_init_hook": "post_init_hook",
Expand Down
254 changes: 1 addition & 253 deletions queue_job/job.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright 2013-2020 Camptocamp
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)

import functools
import hashlib
import inspect
import logging
Expand Down Expand Up @@ -392,7 +391,7 @@ def __init__(
channel=None,
identity_key=None,
):
""" Create a Job
"""Create a Job

:param func: function to execute
:type func: function
Expand Down Expand Up @@ -684,9 +683,6 @@ def __repr__(self):

def _get_retry_seconds(self, seconds=None):
retry_pattern = self.job_config.retry_pattern
if not retry_pattern:
# TODO deprecated by :job-no-decorator:
retry_pattern = getattr(self.func, "retry_pattern", None)
if not seconds and retry_pattern:
# ordered from higher to lower count of retries
patt = sorted(retry_pattern.items(), key=lambda t: t[0])
Expand Down Expand Up @@ -719,13 +715,6 @@ def related_action(self):
return None

funcname = self.job_config.related_action_func_name
if not funcname and hasattr(self.func, "related_action"):
# TODO deprecated by :job-no-decorator:
funcname = self.func.related_action
# decorator is set but empty: disable the default one
if not funcname:
return None

if not funcname:
funcname = record._default_related_action
if not isinstance(funcname, str):
Expand All @@ -735,251 +724,10 @@ def related_action(self):
)
action = getattr(record, funcname)
action_kwargs = self.job_config.related_action_kwargs
if not action_kwargs:
# TODO deprecated by :job-no-decorator:
action_kwargs = getattr(self.func, "kwargs", {})
return action(**action_kwargs)


def _is_model_method(func):
return inspect.ismethod(func) and isinstance(
func.__self__.__class__, odoo.models.MetaModel
)


# TODO deprecated by :job-no-decorator:
def job(func=None, default_channel="root", retry_pattern=None):
"""Decorator for job methods.

Deprecated. Use ``queue.job.function`` XML records (details in
``readme/USAGE.rst``).

It enables the possibility to use a Model's method as a job function.

Optional argument:

:param default_channel: the channel wherein the job will be assigned. This
channel is set at the installation of the module
and can be manually changed later using the views.
:param retry_pattern: The retry pattern to use for postponing a job.
If a job is postponed and there is no eta
specified, the eta will be determined from the
dict in retry_pattern. When no retry pattern
is provided, jobs will be retried after
:const:`RETRY_INTERVAL` seconds.
:type retry_pattern: dict(retry_count,retry_eta_seconds)

Indicates that a method of a Model can be delayed in the Job Queue.

When a method has the ``@job`` decorator, its calls can then be delayed
with::

recordset.with_delay(priority=10).the_method(args, **kwargs)

Where ``the_method`` is the method decorated with ``@job``. Its arguments
and keyword arguments will be kept in the Job Queue for its asynchronous
execution.

``default_channel`` indicates in which channel the job must be executed

``retry_pattern`` is a dict where keys are the count of retries and the
values are the delay to postpone a job.

Example:

.. code-block:: python

class ProductProduct(models.Model):
_inherit = 'product.product'

@job
def export_one_thing(self, one_thing):
# work
# export one_thing

# [...]

env['a.model'].export_one_thing(the_thing_to_export)
# => normal and synchronous function call

env['a.model'].with_delay().export_one_thing(the_thing_to_export)
# => the job will be executed as soon as possible

delayable = env['a.model'].with_delay(priority=30, eta=60*60*5)
delayable.export_one_thing(the_thing_to_export)
# => the job will be executed with a low priority and not before a
# delay of 5 hours from now

@job(default_channel='root.subchannel')
def export_one_thing(one_thing):
# work
# export one_thing

@job(retry_pattern={1: 10 * 60,
5: 20 * 60,
10: 30 * 60,
15: 12 * 60 * 60})
def retryable_example():
# 5 first retries postponed 10 minutes later
# retries 5 to 10 postponed 20 minutes later
# retries 10 to 15 postponed 30 minutes later
# all subsequent retries postponed 12 hours later
raise RetryableJobError('Must be retried later')

env['a.model'].with_delay().retryable_example()


See also: :py:func:`related_action` a related action can be attached
to a job
"""
if func is None:
return functools.partial(
job, default_channel=default_channel, retry_pattern=retry_pattern
)

xml_fields = [
' <field name="model_id" ref="[insert model xmlid]" />\n'
' <field name="method">_test_job</field>\n'
]
if default_channel:
xml_fields.append(' <field name="channel_id" ref="[insert channel xmlid]"/>')
if retry_pattern:
xml_fields.append(' <field name="retry_pattern">{retry_pattern}</field>')

_logger.info(
"@job is deprecated and no longer needed (on %s), it is advised to use an "
"XML record (activate DEBUG log for snippet)",
func.__name__,
)
if _logger.isEnabledFor(logging.DEBUG):
xml_record = (
'<record id="job_function_[insert model]_{method}"'
' model="queue.job.function">\n' + "\n".join(xml_fields) + "\n</record>"
).format(**{"method": func.__name__, "retry_pattern": retry_pattern})
_logger.debug(
"XML snippet (to complete) for replacing @job on %s:\n%s",
func.__name__,
xml_record,
)

def delay_from_model(*args, **kwargs):
raise AttributeError(
"method.delay() can no longer be used, the general form is "
"env['res.users'].with_delay().method()"
)

assert default_channel == "root" or default_channel.startswith(
"root."
), "The channel path must start by 'root'"
assert retry_pattern is None or isinstance(
retry_pattern, dict
), "retry_pattern must be a dict"

delay_func = delay_from_model

func.delayable = True
func.delay = delay_func
func.retry_pattern = retry_pattern
func.default_channel = default_channel
return func


# TODO deprecated by :job-no-decorator:
def related_action(action=None, **kwargs):
"""Attach a *Related Action* to a job (decorator)

Deprecated. Use ``queue.job.function`` XML records (details in
``readme/USAGE.rst``).

A *Related Action* will appear as a button on the Odoo view.
The button will execute the action, usually it will open the
form view of the record related to the job.

The ``action`` must be a method on the `queue.job` model.

Example usage:

.. code-block:: python

class QueueJob(models.Model):
_inherit = 'queue.job'

def related_action_partner(self):
self.ensure_one()
model = self.model_name
partner = self.records
# possibly get the real ID if partner_id is a binding ID
action = {
'name': _("Partner"),
'type': 'ir.actions.act_window',
'res_model': model,
'view_type': 'form',
'view_mode': 'form',
'res_id': partner.id,
}
return action

class ResPartner(models.Model):
_inherit = 'res.partner'

@job
@related_action(action='related_action_partner')
def export_partner(self):
# ...

The kwargs are transmitted to the action:

.. code-block:: python

class QueueJob(models.Model):
_inherit = 'queue.job'

def related_action_product(self, extra_arg=1):
assert extra_arg == 2
model = self.model_name
...

class ProductProduct(models.Model):
_inherit = 'product.product'

@job
@related_action(action='related_action_product', extra_arg=2)
def export_product(self):
# ...

"""

def decorate(func):
related_action_dict = {
"func_name": action,
}
if kwargs:
related_action_dict["kwargs"] = kwargs

xml_fields = (
' <field name="model_id" ref="[insert model xmlid]" />\n'
' <field name="method">_test_job</field>\n'
' <field name="related_action">{related_action}</field>'
)

_logger.info(
"@related_action is deprecated and no longer needed (on %s),"
" it is advised to use an XML record (activate DEBUG log for snippet)",
func.__name__,
)
if _logger.isEnabledFor(logging.DEBUG):
xml_record = (
'<record id="job_function_[insert model]_{method}"'
' model="queue.job.function">\n' + xml_fields + "\n</record>"
).format(**{"method": func.__name__, "related_action": action})
_logger.debug(
"XML snippet (to complete) for replacing @related_action on %s:\n%s",
func.__name__,
xml_record,
)

func.related_action = action
func.kwargs = kwargs
return func

return decorate
19 changes: 0 additions & 19 deletions queue_job/migrations/13.0.1.2.0/post-migrate-queue-job.py

This file was deleted.

17 changes: 1 addition & 16 deletions queue_job/models/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright 2016 Camptocamp
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)

import inspect
import logging
import os

Expand All @@ -21,20 +20,6 @@ class Base(models.AbstractModel):

_inherit = "base"

# TODO deprecated by :job-no-decorator:
def _register_hook(self):
"""Register marked jobs"""
super(Base, self)._register_hook()
job_methods = [
method
for __, method in inspect.getmembers(
self.__class__, predicate=inspect.isfunction
)
if getattr(method, "delayable", None)
]
for job_method in job_methods:
self.env["queue.job.function"]._register_job(self, job_method)

def with_delay(
self,
priority=None,
Expand All @@ -44,7 +29,7 @@ def with_delay(
channel=None,
identity_key=None,
):
""" Return a ``DelayableRecordset``
"""Return a ``DelayableRecordset``

The returned instance allows to enqueue any method of the recordset's
Model.
Expand Down
5 changes: 4 additions & 1 deletion queue_job/models/ir_model_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@
class IrModelFields(models.Model):
_inherit = "ir.model.fields"

ttype = fields.Selection(selection_add=[("job_serialized", "Job Serialized")])
ttype = fields.Selection(
selection_add=[("job_serialized", "Job Serialized")],
ondelete={"job_serialized": "cascade"},
)
Loading