jenkins-bot has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1358142?usp=email )

Change subject: solve_disambiguation: Stream referring pages lazily
......................................................................

solve_disambiguation: Stream referring pages lazily

Collecting all backlinks before yielding delays processing and retains
all referring pages in memory for heavily linked disambiguation pages.

Change-Id: Ie77699e82c3621a3b954004ef6d3386e97bd4cac
---
M scripts/solve_disambiguation.py
M tests/solve_disambiguation_tests.py
2 files changed, 86 insertions(+), 21 deletions(-)

Approvals:
  jenkins-bot: Verified
  Xqt: Looks good to me, approved




diff --git a/scripts/solve_disambiguation.py b/scripts/solve_disambiguation.py
index c60a061..6f9f676 100755
--- a/scripts/solve_disambiguation.py
+++ b/scripts/solve_disambiguation.py
@@ -82,7 +82,7 @@
 import re
 from collections.abc import Generator
 from contextlib import suppress
-from itertools import chain
+from itertools import chain, islice
 from pathlib import Path

 import pywikibot
@@ -411,28 +411,31 @@
         self.main_only = main_only

     def __iter__(self) -> Generator[pywikibot.Page]:
-        """Yield pages."""
-        # TODO: start yielding before all referring pages have been found
-        refs = list(self.page.getReferences(with_template_inclusion=False,
-                                            namespaces=0 if self.main_only
-                                            else None))
-        pywikibot.info(f'Found {len(refs)} references.')
-        # Remove ignorables
+        """Yield eligible pages, buffering only enough to check the minimum."""
         site = self.page.site
-        if site.family.name in ignore_title \
-           and site.lang in ignore_title[site.family.name]:
-            for ig in ignore_title[site.family.name][site.lang]:
-                for i in range(len(refs) - 1, -1, -1):
-                    if re.match(ig, refs[i].title()):
-                        pywikibot.log('Ignoring page ' + refs[i].title())
-                        del refs[i]
-                    elif self.primaryIgnoreManager.isIgnored(refs[i]):
-                        del refs[i]
-        if len(refs) < self.minimum:
+        patterns = ignore_title.get(site.family.name, {}).get(site.lang, [])
+
+        def eligible_pages():
+            for ref in self.page.getReferences(
+                    with_template_inclusion=False,
+                    namespaces=0 if self.main_only else None):
+                for pattern in patterns:
+                    if re.match(pattern, ref.title()):
+                        pywikibot.log('Ignoring page ' + ref.title())
+                        break
+                else:
+                    if not self.primaryIgnoreManager.isIgnored(ref):
+                        yield ref
+
+        refs = eligible_pages()
+        buffered = list(islice(refs, max(0, self.minimum)))
+        if len(buffered) < self.minimum:
             pywikibot.info(
-                f'Found only {len(refs)} pages to work on; skipping.')
+                f'Found only {len(buffered)} pages to work on; skipping.')
             return
-        pywikibot.info(f'Will work on {len(refs)} pages.')
+        pywikibot.info('Working on referring pages.')
+        yield from buffered
+        del buffered
         yield from refs


diff --git a/tests/solve_disambiguation_tests.py 
b/tests/solve_disambiguation_tests.py
index 1afb19d..fff4b55 100755
--- a/tests/solve_disambiguation_tests.py
+++ b/tests/solve_disambiguation_tests.py
@@ -12,10 +12,72 @@
 from unittest.mock import patch

 import pywikibot
-from scripts.solve_disambiguation import DisambiguationRobot
+from scripts.solve_disambiguation import (
+    DisambiguationRobot,
+    ReferringPageGeneratorWithIgnore,
+)
 from tests.aspects import TestCase

 
+class TestReferringPageGenerator(TestCase):
+
+    """Test streaming and the minimum eligible reference count."""
+
+    family = 'wikipedia'
+    code = 'de'
+    dry = True
+
+    def test_streaming(self) -> None:
+        """Yield the first reference without consuming the remaining ones."""
+        page = pywikibot.Page(self.site, 'Disambiguation')
+        ignored = pywikibot.Page(self.site, 'Primary')
+        ref = pywikibot.Page(self.site, 'Article')
+
+        def references():
+            yield ignored
+            yield ref
+            self.fail(
+                'References were consumed before yielding the first page')
+
+        with patch('scripts.solve_disambiguation.ignore_title', {}), \
+                patch.object(page, 'getReferences',
+                             return_value=references()) as get_refs, \
+                patch('scripts.solve_disambiguation.PrimaryIgnoreManager'
+                      '.isIgnored', side_effect=lambda page: page == ignored):
+            gen = iter(ReferringPageGeneratorWithIgnore(page, main_only=True))
+            self.assertEqual(next(gen), ref)
+            get_refs.assert_called_once_with(with_template_inclusion=False,
+                                             namespaces=0)
+            gen.close()
+
+    def test_minimum_after_filtering(self) -> None:
+        """Buffer eligible pages only, and skip if the minimum is unmet."""
+        page = pywikibot.Page(self.site, 'Disambiguation')
+        refs = [
+            pywikibot.Page(self.site, title)
+            for title in ('Ignored', 'Primary', 'First', 'Second', 'Third')]
+        consumed = []
+
+        def references():
+            for ref in refs:
+                consumed.append(ref)
+                yield ref
+
+        with patch('scripts.solve_disambiguation.ignore_title',
+                   {'wikipedia': {'de': ['Ignored']}}), \
+                patch.object(page, 'getReferences',
+                             side_effect=lambda **kwargs: references()), \
+                patch('scripts.solve_disambiguation.PrimaryIgnoreManager'
+                      '.isIgnored', side_effect=lambda ref: ref == refs[1]):
+            gen = iter(ReferringPageGeneratorWithIgnore(page, minimum=2))
+            self.assertEqual(next(gen), refs[2])
+            self.assertEqual(consumed, refs[:4])
+            self.assertEqual(list(gen), refs[3:])
+
+            self.assertEqual(
+                list(ReferringPageGeneratorWithIgnore(page, minimum=4)), [])
+
+
 class TestDisambiguationRobot(TestCase):

     """Test the disambiguation bot."""

--
To view, visit 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1358142?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: Ie77699e82c3621a3b954004ef6d3386e97bd4cac
Gerrit-Change-Number: 1358142
Gerrit-PatchSet: 6
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]

Reply via email to