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

import odoo
from odoo.models import BaseModel

from .exception import FailedJobError, NoSuchJobError, RetryableJobError


def _rebind_to_env(value, new_env):
"""Rebind any BaseModel inside `value` to ``new_env``'s cursor.

Recurses into lists, tuples and dicts. Preserves uid/su/context of each
inner env - only the cursor is swapped. Non-recordset values pass through
untouched.
"""
if isinstance(value, BaseModel):
return value.with_env(value.env(cr=new_env.cr))
if isinstance(value, (list, tuple)):
return type(value)(_rebind_to_env(v, new_env) for v in value)
if isinstance(value, dict):
return {k: _rebind_to_env(v, new_env) for k, v in value.items()}
return value


WAIT_DEPENDENCIES = "wait_dependencies"
PENDING = "pending"
ENQUEUED = "enqueued"
Expand Down Expand Up @@ -533,11 +551,19 @@ 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)
original_args = self.args
original_kwargs = self.kwargs
try:
self._env = env(cr=new_cr)
self.args = tuple(_rebind_to_env(a, self.env) for a in self.args)
self.kwargs = {
k: _rebind_to_env(v, self.env) for k, v in self.kwargs.items()
}
yield
finally:
self._env = env
self.args = original_args
self.kwargs = original_kwargs

def _get_common_dependent_jobs_query(self):
return """
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
9 changes: 9 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,15 @@ 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):
if record.env.cr is not self.env.cr:
raise AssertionError("record argument cursor was not rebound")
if record_list and record_list[0].env.cr is not self.env.cr:
raise AssertionError("record list argument cursor was not rebound")
if record_dict and record_dict["record"].env.cr is not self.env.cr:
raise AssertionError("record dict argument cursor was not rebound")
record.env.cr.commit() # pylint: disable=invalid-commit

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

Expand Down
11 changes: 11 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,16 @@ 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})
)
RunJobController._runjob(self.env, job)
self.assertEqual(job.state, DONE)

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