1515
1616
1717def group (* delayables ):
18+ """Return a group of delayable to form a graph
19+
20+ A group means that jobs can be executed concurrently.
21+ A job or a group of jobs depending on a group can be executed only after
22+ all the jobs of the group are done.
23+
24+ Shortcut to :class:`~odoo.addons.queue_job.delay.DelayableGroup`.
25+
26+ Example::
27+
28+ g1 = group(delayable1, delayable2)
29+ g2 = group(delayable3, delayable4)
30+ g1.done(g2)
31+ g1.delay()
32+ """
1833 return DelayableGroup (* delayables )
1934
2035
2136def chain (* delayables ):
37+ """Return a chain of delayable to form a graph
38+
39+ A chain means that jobs must be executed sequentially.
40+ A job or a group of jobs depending on a group can be executed only after
41+ the last job of the chain is done.
42+
43+ Shortcut to :class:`~odoo.addons.queue_job.delay.DelayableChain`.
44+
45+ Example::
46+
47+ chain1 = chain(delayable1, delayable2, delayable3)
48+ chain2 = chain(delayable4, delayable5, delayable6)
49+ chain1.done(chain2)
50+ chain1.delay()
51+ """
2252 return DelayableChain (* delayables )
2353
2454
2555class Graph :
56+ """Acyclic directed graph holding vertices of any hashable type
57+
58+ This graph is not specifically designed to hold :class:`~Delayable`
59+ instances, although ultimately it is used for this purpose.
60+ """
2661 __slots__ = ('_graph' )
2762
2863 def __init__ (self , graph = None ):
@@ -32,16 +67,26 @@ def __init__(self, graph=None):
3267 self ._graph = {}
3368
3469 def add_vertex (self , vertex ):
70+ """Add a vertex
71+
72+ Has no effect if called several times with the same vertex
73+ """
3574 self ._graph .setdefault (vertex , set ())
3675
3776 def add_edge (self , parent , child ):
77+ """Add an edge between a parent and a child vertex
78+
79+ Has no effect if called several times with the same pair of vertices
80+ """
3881 self .add_vertex (child )
3982 self ._graph .setdefault (parent , set ()).add (child )
4083
4184 def vertices (self ):
85+ """Return the vertices (nodes) of the graph"""
4286 return set (self ._graph )
4387
4488 def edges (self ):
89+ """Return the edges (links) of the graph"""
4590 links = []
4691 for vertex , neighbours in self ._graph .items ():
4792 for neighbour in neighbours :
@@ -58,7 +103,6 @@ def paths(self, vertex):
58103 [[1, 2, 3], [1, 2, 4], [1, 3]]
59104 >>> sorted(self.paths(3))
60105 [[3, 1, 2, 4]]
61-
62106 """
63107 path = [vertex ] # path traversed so far
64108 seen = {vertex } # set of vertices in path
@@ -100,6 +144,10 @@ def topological_sort(self):
100144 queue .append (node )
101145
102146 def root_vertices (self ):
147+ """Returns the root vertices
148+
149+ meaning they do not depend on any other job.
150+ """
103151 dependency_vertices = set ()
104152 for dependencies in self ._graph .values ():
105153 dependency_vertices .update (dependencies )
@@ -117,9 +165,21 @@ def __repr__(self):
117165
118166
119167class DelayableGraph (Graph ):
120- """Directed Graph for Delayable dependencies"""
168+ """Directed Graph for :class:`~Delayable` dependencies
169+
170+ It connects together the :class:`~Delayable`, :class:`~DelayableGroup` and
171+ :class:`~DelayableChain` graphs, and creates then enqueued the jobs.
172+ """
121173
122174 def _merge_graph (self , graph ):
175+ """Merge a graph in the current graph
176+
177+ It takes each vertex, which can be :class:`~Delayable`,
178+ :class:`~DelayableChain` or :class:`~DelayableGroup`, and updates the
179+ current graph with the edges between Delayable objects (connecting
180+ heads and tails of the groups and chains), so that at the end, the
181+ graph contains only Delayable objects and their links.
182+ """
123183 for vertex , neighbours in graph ._graph .items ():
124184 tails = vertex ._tail ()
125185 for tail in tails :
@@ -128,6 +188,11 @@ def _merge_graph(self, graph):
128188 self ._graph .setdefault (tail , set ()).update (heads )
129189
130190 def _connect_graphs (self ):
191+ """Visit the vertices' graphs and connect them, return the whole graph
192+
193+ Build a new graph, walk the vertices and their related vertices, merge
194+ their graph in the new one, until we have visited all the vertices
195+ """
131196 graph = DelayableGraph ()
132197 graph ._merge_graph (self )
133198
@@ -149,6 +214,11 @@ def _connect_graphs(self):
149214 return graph
150215
151216 def _has_to_execute_directly (self , vertices ):
217+ """Used for tests to run tests directly instead of storing them
218+
219+ In tests, prefer to use
220+ :func:`odoo.addons.queue_job.tests.common.mock_jobs`.
221+ """
152222 if os .getenv ('TEST_QUEUE_JOB_NO_DELAY' ):
153223 _logger .warn (
154224 '`TEST_QUEUE_JOB_NO_DELAY` env var found. NO JOB scheduled.'
@@ -166,6 +236,7 @@ def _has_to_execute_directly(self, vertices):
166236
167237 @staticmethod
168238 def _ensure_same_graph_uuid (jobs ):
239+ """Set the same graph uuid on all jobs of the same graph"""
169240 jobs_count = len (jobs )
170241 if jobs_count == 0 :
171242 raise ValueError ("Expecting jobs" )
@@ -189,6 +260,7 @@ def _ensure_same_graph_uuid(jobs):
189260 job .graph_uuid = graph_uuid
190261
191262 def delay (self ):
263+ """Build the whole graph, creates jobs and delay them"""
192264 graph = self ._connect_graphs ()
193265
194266 vertices = graph .vertices ()
@@ -240,6 +312,23 @@ def _execute_graph_direct(self, graph):
240312
241313
242314class DelayableChain :
315+ """Chain of delayables to form a graph
316+
317+ Delayables can be other :class:`~Delayable`, :class:`~DelayableChain` or
318+ :class:`~DelayableGroup` objects.
319+
320+ A chain means that jobs must be executed sequentially.
321+ A job or a group of jobs depending on a group can be executed only after
322+ the last job of the chain is done.
323+
324+ Chains can be connected to other Delayable, DelayableChain or
325+ DelayableGroup objects by using :meth:`~done`.
326+
327+ A Chain is enqueued by calling :meth:`~delay`, which delays the whole
328+ graph.
329+ Important: :meth:`~delay` must be called on the top-level
330+ delayable/chain/group object of the graph.
331+ """
243332 __slots__ = ('_graph' , '__head' , '__tail' )
244333
245334 def __init__ (self , * delayables ):
@@ -263,15 +352,38 @@ def __repr__(self):
263352 return 'DelayableChain({})' .format (self ._graph )
264353
265354 def done (self , * delayables ):
355+ """Connects the current chain to other delayables/chains/groups
356+
357+ The delayables/chains/groups passed in the parameters will be executed
358+ when the current Chain is done.
359+ """
266360 for delayable in delayables :
267361 self ._graph .add_edge (self .__tail , delayable )
268362 return self
269363
270364 def delay (self ):
365+ """Delay the whole graph"""
271366 self ._graph .delay ()
272367
273368
274369class DelayableGroup :
370+ """Group of delayables to form a graph
371+
372+ Delayables can be other :class:`~Delayable`, :class:`~DelayableChain` or
373+ :class:`~DelayableGroup` objects.
374+
375+ A group means that jobs must be executed sequentially.
376+ A job or a group of jobs depending on a group can be executed only after
377+ the all the jobs of the group are done.
378+
379+ Groups can be connected to other Delayable, DelayableChain or
380+ DelayableGroup objects by using :meth:`~done`.
381+
382+ A group is enqueued by calling :meth:`~delay`, which delays the whole
383+ graph.
384+ Important: :meth:`~delay` must be called on the top-level
385+ delayable/chain/group object of the graph.
386+ """
275387 __slots__ = ('_graph' , '_delayables' )
276388
277389 def __init__ (self , * delayables ):
@@ -294,16 +406,46 @@ def __repr__(self):
294406 return 'DelayableGroup({})' .format (self ._graph )
295407
296408 def done (self , * delayables ):
409+ """Connects the current group to other delayables/chains/groups
410+
411+ The delayables/chains/groups passed in the parameters will be executed
412+ when the current Group is done.
413+ """
297414 for parent in self ._delayables :
298415 for child in delayables :
299416 self ._graph .add_edge (parent , child )
300417 return self
301418
302419 def delay (self ):
420+ """Delay the whole graph"""
303421 self ._graph .delay ()
304422
305423
306424class Delayable :
425+ """Unit of a graph, one Delayable will lead to an enqueued job
426+
427+ Delayables can have dependencies on each others, as well as dependencies on
428+ :class:`~DelayableGroup` or :class:`~DelayableChain` objects.
429+
430+ This class will generally not be used directly, it is used internally
431+ by :meth:`~odoo.addons.queue_job.models.base.Base.delayable`. Look
432+ in the base model for more details.
433+
434+ Delayables can be connected to other Delayable, DelayableChain or
435+ DelayableGroup objects by using :meth:`~done`.
436+
437+ Properties of the future job can be set using the :meth:`~set` method,
438+ which always return ``self``::
439+
440+ delayable.set(priority=15).set({"max_retries": 5, "eta": 15}).delay()
441+
442+ It can be used for example to set properties dynamically.
443+
444+ A Delayable is enqueued by calling :meth:`delay()`, which delays the whole
445+ graph.
446+ Important: :meth:`delay()` must be called on the top-level
447+ delayable/chain/group object of the graph.
448+ """
307449 _properties = (
308450 'priority' , 'eta' , 'max_retries' , 'description' ,
309451 'channel' , 'identity_key'
@@ -359,18 +501,28 @@ def _set_from_dict(self, properties):
359501 setattr (self , key , value )
360502
361503 def set (self , * args , ** kwargs ):
504+ """Set job properties and return self
505+
506+ The values can be either a dictionary and/or keywork args
507+ """
362508 if args :
363509 # args must be a dict
364510 self ._set_from_dict (* args )
365511 self ._set_from_dict (kwargs )
366512 return self
367513
368514 def done (self , * delayables ):
515+ """Connects the current Delayable to other delayables/chains/groups
516+
517+ The delayables/chains/groups passed in the parameters will be executed
518+ when the current Delayable is done.
519+ """
369520 for child in delayables :
370521 self ._graph .add_edge (self , child )
371522 return self
372523
373524 def delay (self ):
525+ """Delay the whole graph"""
374526 self ._graph .delay ()
375527
376528 def _build_job (self ):
@@ -419,9 +571,6 @@ class DelayableRecordset(object):
419571 delayable = DelayableRecordset(recordset, priority=20)
420572 delayable.method(args, kwargs)
421573
422- ``method`` must be a method of the recordset's Model, decorated with
423- :func:`~odoo.addons.queue_job.job.job`.
424-
425574 The method call will be processed asynchronously in the job queue, with
426575 the passed arguments.
427576
0 commit comments