Skip to content

Commit 9a1be7d

Browse files
committed
[IMP] orm: add option to iter_browse for yielding chunks
The `iter_browse` class already loads records in chunks, but the iterator returned by `__iter__` always yields single records. Here we add a flag to the constructor (default `False`) that when set to `True` will make `__iter__` return an iterator that yields chunks of the same size as it is loading (i.e. `chunk_size`). closes #362 Related: odoo/upgrade#8987 Signed-off-by: Nicolas Seinlet (nse) <nse@odoo.com>
1 parent c2590fb commit 9a1be7d

File tree

2 files changed

+20
-3
lines changed

2 files changed

+20
-3
lines changed

src/base/tests/test_util.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,19 @@ def test_iter_browse_iter(self):
661661
expected = (len(ids) + chunk_size - 1) // chunk_size
662662
self.assertEqual(read.call_count, expected)
663663

664+
def test_iter_browse_iter_chunks(self):
665+
cr = self.env.cr
666+
cr.execute("SELECT id FROM res_country")
667+
ids = [c for (c,) in cr.fetchall()]
668+
chunk_size = 10
669+
670+
res_chunks = list(
671+
util.iter_browse(self.env["res.country"], ids, logger=None, chunk_size=chunk_size, yield_chunks=True)
672+
)
673+
no_chunks = (len(ids) + chunk_size - 1) // chunk_size
674+
self.assertEqual(len(res_chunks), no_chunks)
675+
self.assertEqual(len(res_chunks[0]), chunk_size)
676+
664677
def test_iter_browse_call(self):
665678
cr = self.env.cr
666679
cr.execute("SELECT id FROM res_country")

src/util/orm.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,8 @@ class iter_browse(object):
377377
:param list(int) ids: list of IDs of the records to iterate
378378
:param int chunk_size: number of records to load in each iteration chunk, `200` by
379379
default
380+
:param bool yield_chunks: when iterating, yield records in chunks of `chunk_size` instead of one by one.
381+
Default is `False`
380382
:param logger: logger used to report the progress, by default
381383
:data:`~odoo.upgrade.util.orm._logger`
382384
:type logger: :class:`logging.Logger`
@@ -387,7 +389,7 @@ class iter_browse(object):
387389
See also :func:`~odoo.upgrade.util.orm.env`
388390
"""
389391

390-
__slots__ = ("_chunk_size", "_cr_uid", "_it", "_logger", "_model", "_patch", "_size", "_strategy")
392+
__slots__ = ("_chunk_size", "_cr_uid", "_it", "_logger", "_model", "_patch", "_size", "_strategy", "_yield_chunks")
391393

392394
def __init__(self, model, *args, **kw):
393395
assert len(args) in [1, 3] # either (cr, uid, ids) or (ids,)
@@ -396,6 +398,7 @@ def __init__(self, model, *args, **kw):
396398
ids = args[-1]
397399
self._size = len(ids)
398400
self._chunk_size = kw.pop("chunk_size", 200) # keyword-only argument
401+
self._yield_chunks = kw.pop("yield_chunks", False)
399402
self._logger = kw.pop("logger", _logger)
400403
self._strategy = kw.pop("strategy", "flush")
401404
assert self._strategy in {"flush", "commit"}
@@ -429,9 +432,10 @@ def __iter__(self):
429432
if self._it is None:
430433
raise RuntimeError("%r ran twice" % (self,))
431434

432-
it = chain.from_iterable(self._it)
435+
it = self._it if self._yield_chunks else chain.from_iterable(self._it)
436+
sz = (self._size + self._chunk_size - 1) // self._chunk_size if self._yield_chunks else self._size
433437
if self._logger:
434-
it = log_progress(it, self._logger, qualifier=self._model._name, size=self._size)
438+
it = log_progress(it, self._logger, qualifier=self._model._name, size=sz)
435439
self._it = None
436440
return chain(it, self._end())
437441

0 commit comments

Comments
 (0)