Skip to content
Draft
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
41 changes: 40 additions & 1 deletion queue_job/job.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,30 @@
from random import randint

import odoo
from odoo.models import BaseModel

from .exception import FailedJobError, NoSuchJobError, RetryableJobError


def _rebind_to_cr(value, cr):
"""Rebind any BaseModel inside ``value`` to the cursor ``cr``.

Recurses into lists, tuples and dicts. Preserves uid/su/context of each
inner env - only the cursor is swapped. Recordsets already bound to
``cr`` and non-recordset values pass through untouched. Containers are
rebuilt, so in-place changes to the result are lost.
"""
if isinstance(value, BaseModel):
if value.env.cr is cr:
return value
return value.with_env(value.env(cr=cr))
if isinstance(value, (list, tuple)):
return type(value)(_rebind_to_cr(v, cr) for v in value)
if isinstance(value, dict):
return {k: _rebind_to_cr(v, cr) for k, v in value.items()}
return value


WAIT_DEPENDENCIES = "wait_dependencies"
PENDING = "pending"
ENQUEUED = "enqueued"
Expand Down Expand Up @@ -533,8 +554,8 @@ def perform(self):
def in_temporary_env(self):
with self.env.registry.cursor() as new_cr:
env = self.env
self._env = env(cr=new_cr)
try:
self._env = env(cr=new_cr)
yield
finally:
self._env = env
Expand Down Expand Up @@ -705,6 +726,24 @@ def env(self):
def _env(self, env):
self.recordset = self.recordset.with_env(env)

@property
def args(self):
"""Positional arguments, rebound to the job's current cursor."""
return _rebind_to_cr(self._args, self.env.cr)

@args.setter
def args(self, value):
self._args = value

@property
def kwargs(self):
"""Keyword arguments, rebound to the job's current cursor."""
return _rebind_to_cr(self._kwargs, self.env.cr)

@kwargs.setter
def kwargs(self, value):
self._kwargs = value

@property
def func(self):
recordset = self.recordset.with_context(job_uuid=self.uuid)
Expand Down
8 changes: 8 additions & 0 deletions test_queue_job/data/queue_job_function_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
<field name="method">job_with_retry_pattern__no_zero</field>
<field name="retry_pattern" eval="{3: 180}" />
</record>
<record
id="job_function_test_queue_job_job_commit_with_arg_records"
model="queue.job.function"
>
<field name="model_id" ref="test_queue_job.model_test_queue_job" />
<field name="method">job_commit_with_arg_records</field>
<field name="allow_commit" eval="True" />
</record>
<record
id="job_function_test_queue_channel_job_sub_channel"
model="queue.job.function"
Expand Down
13 changes: 13 additions & 0 deletions test_queue_job/models/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,19 @@ def job_alter_mutable(self, mutable_arg, mutable_kwarg=None):
mutable_kwarg["b"] = 2
return mutable_arg, mutable_kwarg

def job_commit_with_arg_records(
self, record, record_list=None, record_dict=None, token=None
):
assert record.env.cr is self.env.cr, "record argument cursor was not rebound"
assert (
not record_list or record_list[0].env.cr is self.env.cr
), "record list argument cursor was not rebound"
assert (
not record_dict or record_dict["record"].env.cr is self.env.cr
), "record dict argument cursor was not rebound"
record.env.cr.commit() # pylint: disable=invalid-commit
return token

def delay_me(self, arg, kwarg=None):
return arg, kwarg

Expand Down
12 changes: 12 additions & 0 deletions test_queue_job/tests/test_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import odoo.tests.common as common

from odoo.addons.queue_job import identity_exact
from odoo.addons.queue_job.controllers.main import RunJobController
from odoo.addons.queue_job.delay import DelayableGraph
from odoo.addons.queue_job.exception import (
FailedJobError,
Expand Down Expand Up @@ -68,6 +69,17 @@ def test_perform_args(self):
result = test_job.perform()
self.assertEqual(result, (("o", "k"), {"c": "!"}))

def test_allow_commit_rebinds_recordsets_in_args(self):
record = self.env.user.partner_id
job = (
self.env["test.queue.job"]
.with_delay()
.job_commit_with_arg_records(record, [record], {"record": record}, "ok")
)
RunJobController._runjob(self.env, job)
self.assertEqual(job.state, DONE)
self.assertEqual(job.result, "ok")

def test_retryable_error(self):
test_job = Job(self.method, kwargs={"raise_retry": True}, max_retries=3)
self.assertEqual(test_job.retry, 0)
Expand Down
Loading