jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1340496?usp=email )
Change subject: tools: Initialize the wrapped generator once per lifecycle
......................................................................
tools: Initialize the wrapped generator once per lifecycle
Fetch and validate the same generator object on first iteration, then
reuse it until restart(). Avoid repeatedly evaluating the generator
property and allocating generators which are never consumed.
Validate before storing the generator so failed initialization can be
retried, and report the type of the object actually rejected. Add
focused coverage for lifecycle reuse and invalid initialization.
Change-Id: Ia98dc76afcdf271c847f30340c728b1a7ff22a6d
---
M pywikibot/tools/collections.py
M tests/tools_tests.py
2 files changed, 58 insertions(+), 5 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/tools/collections.py b/pywikibot/tools/collections.py
index ec79239..e694320 100644
--- a/pywikibot/tools/collections.py
+++ b/pywikibot/tools/collections.py
@@ -209,6 +209,9 @@
"""A Generator base class which wraps the internal `generator` property.
+ The wrapped generator is created and validated on first iteration and
+ reused until :meth:`restart` is called.
+
This generator iterator also has :python:`generator.close()
<reference/expressions.html#generator.close>` mixin method and it can
be used as Iterable and Iterator as well.
@@ -272,12 +275,12 @@
:raises TypeError: generator property is not a generator
"""
- if not isinstance(self.generator, Generator):
- raise TypeError('generator property is not a generator but '
- f'{type(self.generator).__name__}')
if not hasattr(self, '_started_gen'):
- # start the generator
- self._started_gen = self.generator
+ generator = self.generator
+ if not isinstance(generator, Generator):
+ raise TypeError('generator property is not a generator but '
+ f'{type(generator).__name__}')
+ self._started_gen = generator
return next(self._started_gen)
@deprecated_args(val='value', tb='traceback') # since 10.7.0
diff --git a/tests/tools_tests.py b/tests/tools_tests.py
index 06a9aca..3827758 100755
--- a/tests/tools_tests.py
+++ b/tests/tools_tests.py
@@ -29,6 +29,7 @@
is_ip_network,
suppress_warnings,
)
+from pywikibot.tools.collections import GeneratorWrapper
from pywikibot.tools.itertools import (
filter_unique,
intersect_generators,
@@ -41,6 +42,55 @@
from tests.utils import skipping
+class TestGeneratorWrapper(TestCase):
+
+ """Test lazy initialization of the wrapped generator."""
+
+ net = False
+
+ def setUp(self) -> None:
+ """Create a wrapper with a tracked generator property."""
+ super().setUp()
+
+ class Wrapper(GeneratorWrapper):
+
+ @property
+ def generator(self):
+ return (i for i in range(3))
+
+ self.wrapper = Wrapper()
+ patcher = mock.patch.object(
+ Wrapper, 'generator', new_callable=mock.PropertyMock)
+ self.generator = patcher.start()
+ self.addCleanup(patcher.stop)
+ self.generator.side_effect = lambda: (i for i in range(3))
+
+ def test_lifecycle(self) -> None:
+ """Fetch the generator once until explicitly restarted."""
+ self.generator.assert_not_called()
+ self.assertEqual(list(self.wrapper), [0, 1, 2])
+ with self.assertRaises(StopIteration):
+ next(self.wrapper)
+ self.generator.assert_called_once_with()
+ self.wrapper.restart()
+ self.assertEqual(next(self.wrapper), 0)
+ self.assertEqual(self.generator.call_count, 2)
+ self.wrapper.close()
+ with self.assertRaises(StopIteration):
+ next(self.wrapper)
+ self.assertEqual(self.generator.call_count, 2)
+
+ def test_invalid_generator(self) -> None:
+ """Reject an invalid result once and allow initialization to retry."""
+ self.generator.side_effect = [42, (i for i in range(3))]
+ with self.assertRaisesRegex(
+ TypeError, '^generator property is not a generator but int$'):
+ next(self.wrapper)
+ self.generator.assert_called_once_with()
+ self.assertEqual(next(self.wrapper), 0)
+ self.assertEqual(self.generator.call_count, 2)
+
+
class OpenArchiveTestCase(TestCase):
"""Unit test class for tools.
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1340496?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.wikimedia.org/r/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: Ia98dc76afcdf271c847f30340c728b1a7ff22a6d
Gerrit-Change-Number: 1340496
Gerrit-PatchSet: 2
Gerrit-Owner: Mahveotm <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]