diff --git a/README.md b/README.md index 4de2448e5e..86db6a28a9 100644 --- a/README.md +++ b/README.md @@ -23,13 +23,13 @@ Available addons addon | version | maintainers | summary --- | --- | --- | --- [base_import_async](base_import_async/) | 18.0.1.0.0 | | Import CSV files in the background -[queue_job](queue_job/) | 18.0.4.1.0 | guewen sbidoul | Job Queue +[queue_job](queue_job/) | 18.0.4.1.2 | guewen sbidoul | Job Queue [queue_job_batch](queue_job_batch/) | 18.0.1.1.1 | | Job Queue Batch [queue_job_cron](queue_job_cron/) | 18.0.1.1.1 | | Scheduled Actions as Queue Jobs [queue_job_cron_jobrunner](queue_job_cron_jobrunner/) | 18.0.1.0.1 | ivantodorovich | Run jobs without a dedicated JobRunner [queue_job_profiler](queue_job_profiler/) | 18.0.1.0.1 | simahawk | Job Queue Profiler [queue_job_subscribe](queue_job_subscribe/) | 18.0.1.0.0 | | Control which users are subscribed to queue job notifications -[test_queue_job](test_queue_job/) | 18.0.2.1.0 | sbidoul | Queue Job Tests +[test_queue_job](test_queue_job/) | 18.0.2.1.1 | sbidoul | Queue Job Tests [test_queue_job_batch](test_queue_job_batch/) | 18.0.1.0.0 | | Test Job Queue Batch [//]: # (end addons) diff --git a/queue_job/README.rst b/queue_job/README.rst index a20c964d6e..7b331118bd 100644 --- a/queue_job/README.rst +++ b/queue_job/README.rst @@ -11,7 +11,7 @@ Job Queue !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:338aa143588050eeb76e4d0a2a40e8a35bfa0263e7c4cba00bfb2d5491696dd5 + !! source digest: sha256:9f83bd687b03da26cd04609397b95315e179cdc7781390f574ef7b2a77061fec !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png diff --git a/queue_job/__manifest__.py b/queue_job/__manifest__.py index c39c9510f3..6b9ef88919 100644 --- a/queue_job/__manifest__.py +++ b/queue_job/__manifest__.py @@ -2,7 +2,7 @@ { "name": "Job Queue", - "version": "18.0.4.1.0", + "version": "18.0.4.1.2", "author": "Camptocamp,ACSONE SA/NV,Odoo Community Association (OCA)", "website": "https://github.com/OCA/queue", "license": "LGPL-3", diff --git a/queue_job/delay.py b/queue_job/delay.py index 0ba54e48a9..516a10aef6 100644 --- a/queue_job/delay.py +++ b/queue_job/delay.py @@ -443,6 +443,7 @@ class Delayable: "recordset", "_graph", "_job_method", + "_job_method_name", "_job_args", "_job_kwargs", "_generated_job", @@ -471,6 +472,7 @@ def __init__( self.identity_key = identity_key self._job_method = None + self._job_method_name = "" self._job_args = () self._job_kwargs = {} @@ -484,8 +486,7 @@ def _tail(self): def __repr__(self): return ( - f"Delayable({self.recordset}." - f"{self._job_method.__name__ if self._job_method else ''}" + f"Delayable({self.recordset}.{self._job_method_name}" f"({self._job_args}, {self._job_kwargs}))" ) @@ -548,7 +549,8 @@ def split(self, size, chain=False): identity_key=self.identity_key, ) # Update the __self__ - delayable._job_method = getattr(recordset, self._job_method.__name__) + delayable._job_method = getattr(recordset, self._job_method_name) + delayable._job_method_name = self._job_method_name delayable._job_args = self._job_args delayable._job_kwargs = self._job_kwargs @@ -557,7 +559,7 @@ def split(self, size, chain=False): description = self.description or ( self._job_method.__doc__.splitlines()[0].strip() if self._job_method.__doc__ - else f"{self.recordset._name}.{self._job_method.__name__}" + else f"{self.recordset._name}.{self._job_method_name}" ) for index, delayable in enumerate(delayables): delayable.set( @@ -574,6 +576,7 @@ def _build_job(self): return self._generated_job self._generated_job = Job( self._job_method, + method_name=self._job_method_name, args=self._job_args, kwargs=self._job_kwargs, priority=self.priority, @@ -599,6 +602,10 @@ def __getattr__(self, name): ) recordset_method = getattr(self.recordset, name) self._job_method = recordset_method + # Keep the name that was looked up: the method found under it may + # carry another __name__ (e.g. replaced by auditlog), and the job must + # resolve the same attribute again when it is loaded and run. + self._job_method_name = name return self._store_args def _execute_direct(self): diff --git a/queue_job/job.py b/queue_job/job.py index a69a71ddc5..736484a81a 100644 --- a/queue_job/job.py +++ b/queue_job/job.py @@ -310,6 +310,7 @@ def _load_from_db_record(cls, job_db_record): job_ = cls( method, + method_name=method_name, args=args, kwargs=kwargs, priority=stored.priority, @@ -385,6 +386,7 @@ def __init__( description=None, channel=None, identity_key=None, + method_name=None, ): """Create a Job @@ -409,6 +411,9 @@ def __init__( :param identity_key: A hash to uniquely identify a job, or a function that returns this hash (the function takes the job as argument) + :param method_name: name of the method on the recordset. Defaults to + ``func.__name__``; pass it when the method was + looked up under a different name than its own. """ if args is None: args = () @@ -424,7 +429,7 @@ def __init__( raise TypeError("Job accepts only methods of Models") recordset = func.__self__ - self.method_name = func.__name__ + self.method_name = method_name or func.__name__ self.recordset = recordset self.job_config = ( @@ -781,7 +786,7 @@ def description(self): elif self.func.__doc__: return self.func.__doc__.splitlines()[0].strip() else: - return f"{self.model_name}.{self.func.__name__}" + return f"{self.model_name}.{self.method_name}" @property def uuid(self): diff --git a/queue_job/jobrunner/__init__.py b/queue_job/jobrunner/__init__.py index 2a1168707b..66575184c8 100644 --- a/queue_job/jobrunner/__init__.py +++ b/queue_job/jobrunner/__init__.py @@ -147,7 +147,8 @@ def prefork_worker_pop(server, pid): def threaded_start(server, *args, **kwargs): res = orig_threaded_start(server, *args, **kwargs) - _start_runner_thread("threaded server") + if not kwargs.get("stop"): + _start_runner_thread("threaded server") return res diff --git a/queue_job/static/description/index.html b/queue_job/static/description/index.html index 495f766a19..9081e08791 100644 --- a/queue_job/static/description/index.html +++ b/queue_job/static/description/index.html @@ -372,7 +372,7 @@

Job Queue

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:338aa143588050eeb76e4d0a2a40e8a35bfa0263e7c4cba00bfb2d5491696dd5 +!! source digest: sha256:9f83bd687b03da26cd04609397b95315e179cdc7781390f574ef7b2a77061fec !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Mature License: LGPL-3 OCA/queue Translate me on Weblate Try me on Runboat

This addon adds an integrated Job Queue to Odoo.

diff --git a/test_queue_job/__manifest__.py b/test_queue_job/__manifest__.py index e49661b6a3..f0b093bcca 100644 --- a/test_queue_job/__manifest__.py +++ b/test_queue_job/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Queue Job Tests", - "version": "18.0.2.1.0", + "version": "18.0.2.1.1", "author": "Camptocamp,Odoo Community Association (OCA)", "license": "LGPL-3", "category": "Generic Modules", diff --git a/test_queue_job/tests/test_delayable.py b/test_queue_job/tests/test_delayable.py index 3bd6cfd5cd..7806c7c434 100644 --- a/test_queue_job/tests/test_delayable.py +++ b/test_queue_job/tests/test_delayable.py @@ -14,6 +14,7 @@ chain, group, ) +from odoo.addons.queue_job.job import Job class TestDelayable(common.TransactionCase): @@ -308,3 +309,38 @@ def test_delay_job_already_exists(self): node.on_done(node2).delay() self.assert_generated_job(node, node2) self.assert_dependencies({node: {}, node2: {node}}) + + def _replace_testing_method_under_another_name(self): + """Put a function named ``testing_method_renamed`` into the + ``testing_method`` slot, the way auditlog replaces write and unlink.""" + cls = type(self.test_model) + original = cls.testing_method + + def testing_method_renamed(self, *args, **kwargs): + return original(self, *args, **kwargs) + + self.patch(cls, "testing_method", testing_method_renamed) + + def test_delayable_keeps_requested_method_name(self): + self._replace_testing_method_under_another_name() + node = Delayable(self.test_model).testing_method(1) + node.delay() + job = node._generated_job + self.assertEqual(job.method_name, "testing_method") + self.assertEqual(job.db_record().method_name, "testing_method") + # Loading the job resolves the same name again + loaded = Job.load(self.env, job.uuid) + self.assertEqual(loaded.method_name, "testing_method") + + def test_with_delay_keeps_requested_method_name(self): + self._replace_testing_method_under_another_name() + job = self.test_model.with_delay().testing_method(1) + self.assertEqual(job.method_name, "testing_method") + + def test_delayable_split_keeps_requested_method_name(self): + self._replace_testing_method_under_another_name() + records = self.test_model.create([{"name": "a"}, {"name": "b"}]) + group = Delayable(records).testing_method(1).split(1) + group.delay() + for node in group._delayables: + self.assertEqual(node._generated_job.method_name, "testing_method")