jenkins-bot has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/812372 )

Change subject: [IMPR] Provide a Generator wrapper class to start/restart a 
generator
......................................................................

[IMPR] Provide a Generator wrapper class to start/restart a generator

- Make all api generators a collections.abc.Generator
- Make EventStreams a a collections.abc.GeneratorGenerator
- add an abstract api.APIGeneratorBase class which replaces the
  _RequestWrapper class. This base class provide all methods from
  _RequestWrapper but also a abstract generator method to be used
  with the send() and throw() method which defines the Generator.
  The close() method is inherited from collections.abc.generator
  as well as Iterator and Iterable functionality. A restart()
  method was added which is able to restart the generator.
- rename all '__iter__' methods to generator property to be used
  with the Generator methods above.
- show an info if a Iterable is wrapped into a generator in
  BaseBot.run()
- remove iter() function for every generator based on APIGeneratorBase
- update tests
- shorten boolean test in thanks_tests.py and flow_thanks_tests.py
- remove TestRecentChanges.setUpClass because imagepage isn't used
- enable doctest for eventstreams.py

Bug: T301318
Bug: T312654
Bug: T312883
Change-Id: I92941d18ad71f10b04a3c7f7d7bbbd4460e9c861
---
M pywikibot/bot.py
M pywikibot/comms/eventstreams.py
M pywikibot/data/api/__init__.py
M pywikibot/data/api/_generators.py
M pywikibot/login.py
M pywikibot/page/_user.py
M pywikibot/tools/collections.py
M scripts/checkimages.py
M tests/api_tests.py
M tests/flow_thanks_tests.py
M tests/logentries_tests.py
M tests/proofreadpage_tests.py
M tests/site_tests.py
M tests/thanks_tests.py
M tox.ini
15 files changed, 283 insertions(+), 119 deletions(-)

Approvals:
  Matěj Suchánek: Looks good to me, but someone else must approve
  Xqt: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/pywikibot/bot.py b/pywikibot/bot.py
index 91b252a..19a3f24 100644
--- a/pywikibot/bot.py
+++ b/pywikibot/bot.py
@@ -1642,6 +1642,8 @@
                                       .format(self.__class__.__name__))
         if not isinstance(self.generator, Generator):
             # to provide close() method
+            pywikibot.info('wrapping {} type to a Generator type'
+                           .format(type(self.generator).__name__))
             self.generator = (item for item in self.generator)
         try:
             for item in self.generator:
diff --git a/pywikibot/comms/eventstreams.py b/pywikibot/comms/eventstreams.py
index 2f1ce69..eed7350 100644
--- a/pywikibot/comms/eventstreams.py
+++ b/pywikibot/comms/eventstreams.py
@@ -25,6 +25,7 @@

 from pywikibot import Site, Timestamp, config, debug, warning
 from pywikibot.tools import cached
+from pywikibot.tools.collections import GeneratorWrapper


 try:
@@ -39,36 +40,73 @@
         "install it with 'pip install \"requests>=2.20.1\"'\n")


-class EventStreams:
+class EventStreams(GeneratorWrapper):

-    """Basic EventStreams iterator class for Server-Sent Events (SSE) protocol.
+    """Generator class for Server-Sent Events (SSE) protocol.

     It provides access to arbitrary streams of data including recent changes.

     Usage:

     >>> stream = EventStreams(streams='recentchange')
-    >>> stream.register_filter(type='edit', wiki='wikidatawiki')
-    >>> change = next(iter(stream))
-    >>> print('{type} on page {title} by {user}.'.format_map(change))
-    edit on page Q32857263 by XXN-bot.
-    >>> change
-    {'comment': '/* wbcreateclaim-create:1| */ [[Property:P31]]: [[Q4167836]]',
-     'wiki': 'wikidatawiki', 'type': 'edit', 'server_name': 'www.wikidata.org',
-     'server_script_path': '/w', 'namespace': 0, 'title': 'Q32857263',
-     'bot': True, 'server_url': 'https://www.wikidata.org',
-     'length': {'new': 1223, 'old': 793},
-     'meta': {'domain': 'www.wikidata.org', 'partition': 0,
-              'uri': 'https://www.wikidata.org/wiki/Q32857263',
-              'offset': 288986585, 'topic': 'eqiad.mediawiki.recentchange',
-              'request_id': '1305a006-8204-4f51-a27b-0f2df58289f4',
-              'schema_uri': 'mediawiki/recentchange/1',
-              'dt': '2017-07-13T10:55:31+00:00',
-              'id': 'ca13742b-67b9-11e7-935d-141877614a33'},
-     'user': 'XXN-bot', 'timestamp': 1499943331, 'patrolled': True,
-     'id': 551158959, 'minor': False,
-     'revision': {'new': 518751558, 'old': 517180066}}
+    >>> stream.register_filter(type='edit', wiki='wikidatawiki', bot=True)
+    >>> change = next(stream)
+    >>> msg = '{type} on page {title} by {user}.'.format_map(change)
+    >>> print(msg)  # doctest: +SKIP
+    edit on page Q2190037 by KrBot.
+    >>> from pprint import pprint
+    >>> pprint(change, width=75)  # doctest: +SKIP
+    {'$schema': '/mediawiki/recentchange/1.0.0',
+     'bot': True,
+     'comment': '/* wbsetreference-set:2| */ [[Property:P10585]]: 96FPN, см. '
+                '/ see [[Template:Autofix|autofix]] на / on [[Property '
+                'talk:P356]]',
+     'id': 1728475074,
+     'length': {'new': 8871, 'old': 8871},
+     'meta': {'domain': 'www.wikidata.org',
+              'dt': '2022-07-12T17:54:15Z',
+              'id': '2cdec62f-a2b3-49b8-9a52-85a42236fb99',
+              'offset': 4000957901,
+              'partition': 0,
+              'request_id': 'f7896e77-fd2b-4a95-a9e4-44c1e3ad818b',
+              'stream': 'mediawiki.recentchange',
+              'topic': 'eqiad.mediawiki.recentchange',
+              'uri': 'https://www.wikidata.org/wiki/Q2190037'},
+     'minor': False,
+     'namespace': 0,
+     'parsedcomment': '\u200e<span dir="auto"><span '
+                      'class="autocomment">Изменена ссылка на заявление: '
+                      '</span></span> <a href="/wiki/Property:P10585" '
+                      'title="Property:P10585">Property:P10585</a>: 96FPN, '
+                      'см. / see <a href="/wiki/Template:Autofix" '
+                      'title="Template:Autofix">autofix</a> на / on <a '
+                      'href="/wiki/Property_talk:P356" title="Property '
+                      'talk:P356">Property talk:P356</a>',
+     'patrolled': True,
+     'revision': {'new': 1676015019, 'old': 1675697125},
+     'server_name': 'www.wikidata.org',
+     'server_script_path': '/w',
+     'server_url': 'https://www.wikidata.org',
+     'timestamp': 1657648455,
+     'title': 'Q2190037',
+     'type': 'edit',
+     'user': 'KrBot',
+     'wiki': 'wikidatawiki'}
+    >>> pprint(next(stream), width=75)  # doctest: +ELLIPSIS
+    {'$schema': '/mediawiki/recentchange/1.0.0',
+     'bot': True,
+     ...
+     'server_name': 'www.wikidata.org',
+     'server_script_path': '/w',
+     'server_url': 'https://www.wikidata.org',
+     ...
+     'type': 'edit',
+     'user': '...',
+     'wiki': 'wikidatawiki'}
     >>> del stream
+
+    .. versionchanged:: 7.6
+       subclassed from :class:`pywikibot.tools.collections.GeneratorWrapper`
     """

     def __init__(self, **kwargs) -> None:
@@ -273,8 +311,13 @@
             return True
         return any(function(data) for function in self.filter['any'])

-    def __iter__(self):
-        """Iterator."""
+    @property
+    def generator(self):
+        """Inner generator.
+
+        .. versionchanged:: 7.6
+           changed from iterator method to generator property
+        """
         n = 0
         event = None
         ignore_first_empty_warning = True
diff --git a/pywikibot/data/api/__init__.py b/pywikibot/data/api/__init__.py
index 7f5b15c..dbd6b4c 100644
--- a/pywikibot/data/api/__init__.py
+++ b/pywikibot/data/api/__init__.py
@@ -10,6 +10,7 @@

 from pywikibot.comms import http
 from pywikibot.data.api._generators import (
+    APIGeneratorBase,
     APIGenerator,
     ListGenerator,
     LogEntryListGenerator,
@@ -25,6 +26,7 @@
 from pywikibot.family import SubdomainFamily

 __all__ = (
+    'APIGeneratorBase',
     'APIGenerator',
     'CachedRequest',
     'ListGenerator',
diff --git a/pywikibot/data/api/_generators.py 
b/pywikibot/data/api/_generators.py
index 2f35937..e3a1f21 100644
--- a/pywikibot/data/api/_generators.py
+++ b/pywikibot/data/api/_generators.py
@@ -1,15 +1,23 @@
-"""Objects representing API/Query generators."""
+"""Objects representing API/Query generators.
+
+.. versionchanged:: 7.6
+   All Objects were changed from Iterable object to a Generator object.
+   They are subclassed from
+   :class:`pywikibot.tools.collections.GeneratorWrapper`
+"""
 #
 # (C) Pywikibot team, 2008-2022
 #
 # Distributed under the terms of the MIT license.
 #
+from abc import abstractmethod, ABC
 from typing import Union
 from warnings import warn

 import pywikibot
 from pywikibot import config
 from pywikibot.exceptions import Error, InvalidTitleError, UnsupportedPageError
+from pywikibot.tools.collections import GeneratorWrapper

 __all__ = (
     'APIGenerator',
@@ -22,9 +30,13 @@
 )


-class _RequestWrapper:
+class APIGeneratorBase(ABC):

-    """A wrapper class to handle the usage of the ``parameters`` parameter."""
+    """A wrapper class to handle the usage of the ``parameters`` parameter.
+
+    .. versionchanged:: 7.6
+       renamed from _RequestWrapper
+    """

     def _clean_kwargs(self, kwargs, **mw_api_args):
         """Clean kwargs, define site and request class."""
@@ -39,19 +51,28 @@
         kwargs['parameters'].update(mw_api_args)
         return kwargs

+    @abstractmethod
     def set_maximum_items(self, value: Union[int, str, None]) -> None:
+        """Set the maximum number of items to be retrieved from the wiki.
+
+        .. versionadded:: 7.1
+        .. versionchanged:: 7.6
+           become an abstract method
+        """
         raise NotImplementedError


-class APIGenerator(_RequestWrapper):
+class APIGenerator(APIGeneratorBase, GeneratorWrapper):

-    """
-    Iterator that handle API responses containing lists.
+    """Generator that handle API responses containing lists.

-    The iterator will iterate each item in the query response and use the
-    continue request parameter to retrieve the next portion of items
+    The generator will iterate each item in the query response and use
+    the continue request parameter to retrieve the next portion of items
     automatically. If the limit attribute is set, the iterator will stop
     after iterating that many values.
+
+    .. versionchanged:: 7.6
+       subclassed from :class:`pywikibot.tools.collections.GeneratorWrapper`
     """

     def __init__(
@@ -121,11 +142,15 @@
             pywikibot.debug('{}: Set limit (maximum_items) to {}.'
                             .format(type(self).__name__, self.limit))

-    def __iter__(self):
+    @property
+    def generator(self):
         """
         Submit request and iterate the response.

         Continues response as needed until limit (if defined) is reached.
+
+        .. versionchanged:: 7.6
+           changed from iterator method to generator property
         """
         offset = self.starting_offset
         n = 0
@@ -154,23 +179,26 @@
                 break


-class QueryGenerator(_RequestWrapper):
+class QueryGenerator(APIGeneratorBase, GeneratorWrapper):

-    """
-    Base class for iterators that handle responses to API action=query.
+    """Base class for generators that handle responses to API action=query.

-    By default, the iterator will iterate each item in the query response,
-    and use the (query-)continue element, if present, to continue iterating as
-    long as the wiki returns additional values. However, if the iterator's
-    limit attribute is set to a positive int, the iterator will stop after
-    iterating that many values. If limit is negative, the limit parameter
-    will not be passed to the API at all.
+    By default, the generator will iterate each item in the query
+    response, and use the (query-)continue element, if present, to
+    continue iterating as long as the wiki returns additional values.
+    However, if the generators's limit attribute is set to a positive
+    int, the generators will stop after iterating that many values. If
+    limit is negative, the limit parameter will not be passed to the API
+    at all.

-    Most common query types are more efficiently handled by subclasses, but
-    this class can be used directly for custom queries and miscellaneous
-    types (such as "meta=...") that don't return the usual list of pages or
-    links. See the API documentation for specific query options.
+    Most common query types are more efficiently handled by subclasses,
+    but this class can be used directly for custom queries and
+    miscellaneous types (such as "meta=...") that don't return the usual
+    list of pages or links. See the API documentation for specific query
+    options.

+    .. versionchanged:: 7.6
+       subclassed from :class:`pywikibot.tools.collections.GeneratorWrapper`
     """

     # Should results be filtered during iteration according to set_namespace?
@@ -560,11 +588,14 @@
                 raise RuntimeError(
                     'QueryGenerator._extract_results reached the limit')

-    def __iter__(self):
+    @property
+    def generator(self):
         """Submit request and iterate the response based on self.resultkey.

         Continues response as needed until limit (if any) is reached.

+        .. versionchanged:: 7.6
+           changed from iterator method to generator property
         """
         previous_result_had_data = True
         prev_limit = new_limit = None
@@ -627,12 +658,11 @@

 class PageGenerator(QueryGenerator):

-    """Iterator for response to a request of type action=query&generator=foo.
+    """Generator for response to a request of type action=query&generator=foo.

-    This class can be used for any of the query types that are listed in the
-    API documentation as being able to be used as a generator. Instances of
-    this class iterate Page objects.
-
+    This class can be used for any of the query types that are listed in
+    the API documentation as being able to be used as a generator.
+    Instances of this class iterate Page objects.
     """

     def __init__(
@@ -700,17 +730,16 @@

 class PropertyGenerator(QueryGenerator):

-    """Iterator for queries of type action=query&prop=foo.
+    """Generator for queries of type action=query&prop=foo.

     See the API documentation for types of page properties that can be
     queried.

-    This iterator yields one or more dict object(s) corresponding
-    to each "page" item(s) from the API response; the calling module has to
+    This generator yields one or more dict object(s) corresponding to
+    each "page" item(s) from the API response; the calling module has to
     decide what to do with the contents of the dict. There will be one
     dict for each page queried via a titles= or ids= parameter (which must
     be supplied when instantiating this class).
-
     """

     def __init__(self, prop: str, **kwargs) -> None:
@@ -732,10 +761,15 @@
         """The requested property names."""
         return self._props

-    def __iter__(self):
-        """Yield results."""
+    @property
+    def generator(self):
+        """Yield results.
+
+        .. versionchanged:: 7.6
+           changed from iterator method to generator property
+        """
         self._previous_dicts = {}
-        yield from super().__iter__()
+        yield from super().generator
         yield from self._previous_dicts.values()

     def _extract_results(self, resultdata):
@@ -775,18 +809,18 @@

 class ListGenerator(QueryGenerator):

-    """Iterator for queries of type action=query&list=foo.
+    """Generator for queries of type action=query&list=foo.

-    See the API documentation for types of lists that can be queried. Lists
-    include both site-wide information (such as 'allpages') and page-specific
-    information (such as 'backlinks').
+    See the API documentation for types of lists that can be queried.
+    Lists include both site-wide information (such as 'allpages') and
+    page-specific information (such as 'backlinks').

-    This iterator yields a dict object for each member of the list returned
-    by the API, with the format of the dict depending on the particular list
-    command used. For those lists that contain page information, it may be
-    easier to use the PageGenerator class instead, as that will convert the
-    returned information into a Page object.
-
+    This generator yields a dict object for each member of the list
+    returned by the API, with the format of the dict depending on the
+    particular list command used. For those lists that contain page
+    information, it may be easier to use the PageGenerator class
+    instead, as that will convert the returned information into a Page
+    object.
     """

     def __init__(self, listaction: str, **kwargs) -> None:
@@ -804,8 +838,7 @@

 class LogEntryListGenerator(ListGenerator):

-    """
-    Iterator for queries of list 'logevents'.
+    """Generator for queries of list 'logevents'.

     Yields LogEntry objects instead of dicts.
     """
diff --git a/pywikibot/login.py b/pywikibot/login.py
index d14195e..2bbbebd 100644
--- a/pywikibot/login.py
+++ b/pywikibot/login.py
@@ -132,7 +132,7 @@

         try:
             data = self.site.allusers(start=main_username, total=1)
-            user = next(iter(data))
+            user = next(data)
         except APIError as e:
             if e.code == 'readapidenied':
                 pywikibot.warning("Could not check user '{}' exists on {}"
diff --git a/pywikibot/page/_user.py b/pywikibot/page/_user.py
index b095775..22c4385 100644
--- a/pywikibot/page/_user.py
+++ b/pywikibot/page/_user.py
@@ -320,7 +320,7 @@
         :return: last user log entry
         :rtype: LogEntry or None
         """
-        return next(iter(self.logevents(total=1)), None)
+        return next(self.logevents(total=1), None)

     def contributions(self, total: int = 500, **kwargs) -> tuple:
         """
diff --git a/pywikibot/tools/collections.py b/pywikibot/tools/collections.py
index e06d070..a6934dc 100644
--- a/pywikibot/tools/collections.py
+++ b/pywikibot/tools/collections.py
@@ -5,15 +5,28 @@
 # Distributed under the terms of the MIT license.
 #
 import collections
-from collections.abc import Container, Iterable, Iterator, Mapping, Sized
+
+from abc import abstractmethod, ABC
+from collections.abc import (
+    Container,
+    Generator,
+    Iterable,
+    Iterator,
+    Mapping,
+    Sized,
+)
 from contextlib import suppress
 from itertools import chain
+from typing import Any
+
+from pywikibot.backports import Generator as GeneratorType


 __all__ = (
     'CombinedError',
     'DequeGenerator',
     'EmptyDefault',
+    'GeneratorWrapper',
     'SizedKeyCollection',
     'EMPTY_DEFAULT',
 )
@@ -193,3 +206,96 @@
         result = '{}({})'.format(self.__class__.__name__, items)
         self.extend(items)
         return result
+
+
+class GeneratorWrapper(ABC, Generator):
+
+    """A Generator base class which wraps the internal `generator` property.
+
+    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.
+
+    .. versionadded:: 7.6
+
+    Example:
+
+    >>> class Gen(GeneratorWrapper):
+    ...     @property
+    ...     def generator(self):
+    ...         return (c for c in 'Pywikibot')
+    >>> gen = Gen()
+    >>> next(gen)  # can be used as Iterator ...
+    'P'
+    >>> next(gen)
+    'y'
+    >>> ''.join(c for c in gen)  # ... or as Iterable
+    'wikibot'
+    >>> next(gen)  # the generator is exhausted ...
+    Traceback (most recent call last):
+        ...
+    StopIteration
+    >>> gen.restart()  # ... but can be restarted
+    >>> next(gen) + next(gen)
+    'Py'
+    >>> gen.close()  # the generator may be closed
+    >>> next(gen)
+    Traceback (most recent call last):
+        ...
+    StopIteration
+    >>> gen.restart()  # restart a closed generator
+    >>> # also send() and throw() works
+    >>> gen.send(None) + gen.send(None)
+    'Py'
+    >>> gen.throw(RuntimeError('Foo'))
+    Traceback (most recent call last):
+        ...
+    RuntimeError: Foo
+
+    .. seealso:: :pep:`342`
+    """
+
+    @abstractmethod
+    def generator(self) -> GeneratorType[Any, Any, Any]:
+        """Abstract generator property."""
+        return iter(())
+
+    def send(self, value: Any) -> Any:
+        """Return next yielded value from generator or raise StopIteration.
+
+        The `value` parameter is ignored yet; usually it should be ``None``.
+        If the wrapped generator property exits without yielding another
+        value this method raises `StopIteration`. The send method works
+        like the `next`function with a GeneratorWrapper instance as
+        parameter.
+
+        Refer :python:`generator.send()
+        <reference/expressions.html#generator.send>` for its usage.
+
+        :raises TypeError: generator property is not a generator
+        """
+        if not isinstance(self.generator, GeneratorType):
+            raise TypeError('generator property is not a generator but {}'
+                            .format(type(self.generator).__name__))
+        if not hasattr(self, '_started_gen'):
+            # start the generator
+            self._started_gen = self.generator
+        return next(self._started_gen)
+
+    def throw(self, typ: Exception, val=None, tb=None) -> None:
+        """Raise an exception inside the wrapped generator.
+
+        Refer :python:`generator.throw()
+        <reference/expressions.html#generator.throw>` for various
+        parameter usage.
+
+        :raises RuntimeError: No generator started
+        """
+        if not hasattr(self, '_started_gen'):
+            raise RuntimeError('No generator was started')
+        self._started_gen.throw(typ, val, tb)
+
+    def restart(self) -> None:
+        """Restart the generator."""
+        with suppress(AttributeError):
+            del self._started_gen
diff --git a/scripts/checkimages.py b/scripts/checkimages.py
index ef83170..0113c9b 100755
--- a/scripts/checkimages.py
+++ b/scripts/checkimages.py
@@ -833,7 +833,7 @@

         site = pywikibot.Site('commons')
         commons_image_with_this_hash = next(
-            iter(site.allimages(sha1=hash_found, total=1)), None)
+            site.allimages(sha1=hash_found, total=1), None)
         if commons_image_with_this_hash:
             service_template = pywikibot.translate(self.site,
                                                    SERVICE_TEMPLATES)
diff --git a/tests/api_tests.py b/tests/api_tests.py
index be3a6e4..30b629a 100755
--- a/tests/api_tests.py
+++ b/tests/api_tests.py
@@ -500,6 +500,7 @@
             with self.subTest(amount=i):
                 self.gen.set_maximum_items(i)
                 self.assertPageTitlesEqual(self.gen, self.titles[:i])
+                self.gen.restart()

     def test_limit_zero(self):
         """Test that a limit of zero is the same as limit None."""
diff --git a/tests/flow_thanks_tests.py b/tests/flow_thanks_tests.py
index b4a83f5..acdd8cb 100755
--- a/tests/flow_thanks_tests.py
+++ b/tests/flow_thanks_tests.py
@@ -47,13 +47,7 @@
         post.thank()
         log_entries = site.logevents(logtype='thanks', total=5, page=user,
                                      start=before_time, reverse=True)
-        try:
-            next(iter(log_entries))
-        except StopIteration:
-            found_log = False
-        else:
-            found_log = True
-        self.assertTrue(found_log)
+        self.assertTrue(bool(next(log_entries, None)))

     def test_self_thank(self):
         """Test that thanking one's own Flow post causes an error."""
diff --git a/tests/logentries_tests.py b/tests/logentries_tests.py
index a996cbd..e4468c3 100755
--- a/tests/logentries_tests.py
+++ b/tests/logentries_tests.py
@@ -19,6 +19,7 @@
 )
 from tests import unittest_print
 from tests.aspects import MetaTestCaseClass, TestCase
+from tests.utils import skipping


 class TestLogentriesBase(TestCase):
@@ -64,10 +65,10 @@
             # MW versions and otherwise it might not be visible that the test
             # isn't run on an older wiki.
             self.assertLess(self.site.mw_version, '1.25')
-        try:
-            le = next(iter(self.site.logevents(logtype=logtype, total=1)))
-        except StopIteration:
-            self.skipTest('No entry found for {!r}'.format(logtype))
+
+        with skipping(StopIteration,
+                      msg='No entry found for {!r}'.format(logtype)):
+            le = next(self.site.logevents(logtype=logtype, total=1))
         return le

     def _test_logevent(self, logtype):
@@ -271,11 +272,11 @@
         """Test equality of LogEntry instances."""
         site = self.get_site('dewp')
         other_site = self.get_site('tewp')
-        gen1 = iter(site.logevents(reverse=True, total=2))
-        gen2 = iter(site.logevents(reverse=True, total=2))
+        gen1 = site.logevents(reverse=True, total=2)
+        gen2 = site.logevents(reverse=True, total=2)
         le1 = next(gen1)
         le2 = next(gen2)
-        le3 = next(iter(other_site.logevents(reverse=True, total=1)))
+        le3 = next(other_site.logevents(reverse=True, total=1))
         le4 = next(gen1)
         le5 = next(gen2)
         self.assertEqual(le1, le2)
diff --git a/tests/proofreadpage_tests.py b/tests/proofreadpage_tests.py
index 8f99e79..ad2f675 100755
--- a/tests/proofreadpage_tests.py
+++ b/tests/proofreadpage_tests.py
@@ -297,7 +297,7 @@
         rvgen.set_maximum_items(-1)  # suppress use of rvlimit parameter

         try:
-            pagedict = next(iter(rvgen))
+            pagedict = next(rvgen)
             loaded_text = pagedict.get('revisions')[0].get('*')
         except (StopIteration, TypeError, KeyError, ValueError, IndexError):
             loaded_text = ''
diff --git a/tests/site_tests.py b/tests/site_tests.py
index fcb9338..4d70509 100755
--- a/tests/site_tests.py
+++ b/tests/site_tests.py
@@ -1075,10 +1075,10 @@
         with skipping(
             StopIteration,
                 msg='No images on the main page of site {!r}'.format(mysite)):
-            imagepage = next(iter(page.imagelinks()))  # 1st image of page
+            imagepage = next(page.imagelinks())  # 1st image of page

-        pywikibot.output('site_tests.TestImageUsage found {} on {}'
-                         .format(imagepage, page))
+        unittest_print('site_tests.TestImageUsage found {} on {}'
+                       .format(imagepage, page))

         self.__class__._image_page = imagepage
         return imagepage
@@ -1234,19 +1234,6 @@

     """Test recentchanges method."""

-    @classmethod
-    def setUpClass(cls):
-        """Test up test class."""
-        super().setUpClass()
-        mysite = cls.get_site()
-        try:
-            # 1st image on main page
-            imagepage = next(iter(mysite.allimages()))
-        except StopIteration:
-            unittest_print('No images on site {!r}'.format(mysite))
-            imagepage = None
-        cls.imagepage = imagepage
-
     def test_basic(self):
         """Test the site.recentchanges() method."""
         mysite = self.site
@@ -2719,7 +2706,7 @@
         """Test properties."""
         gen = self.site.filearchive(prop=['sha1', 'size', 'user'], total=1)
         self.assertIn('faprop=sha1|size|user', str(gen.request))
-        item = next(iter(gen))
+        item = next(gen)
         self.assertIn('sha1', item)
         self.assertIn('size', item)
         self.assertIn('user', item)
@@ -2730,8 +2717,8 @@
         gen2 = self.site.filearchive(reverse=True, total=1)
         self.assertNotIn('fadir=', str(gen1.request))
         self.assertIn('fadir=descending', str(gen2.request))
-        fa1 = next(iter(gen1))
-        fa2 = next(iter(gen2))
+        fa1 = next(gen1)
+        fa2 = next(gen2)
         self.assertLess(fa1['name'], fa2['name'])

     def test_filearchive_start(self):
@@ -2739,7 +2726,7 @@
         gen = self.site.filearchive(start='py', end='wiki', total=1)
         self.assertIn('fafrom=py', str(gen.request))
         self.assertIn('fato=wiki', str(gen.request))
-        item = next(iter(gen))
+        item = next(gen)
         self.assertGreaterEqual(item['name'], 'Py')

     def test_filearchive_sha1(self):
@@ -2747,7 +2734,7 @@
         sha1 = '0d5a00aa774100408e60da09f5fb21f253b366f1'
         gen = self.site.filearchive(sha1=sha1, prop='sha1', total=1)
         self.assertIn('fasha1=' + sha1, str(gen.request))
-        item = next(iter(gen))
+        item = next(gen)
         self.assertEqual(item['sha1'], sha1)


diff --git a/tests/thanks_tests.py b/tests/thanks_tests.py
index c670392..08a3f3e 100755
--- a/tests/thanks_tests.py
+++ b/tests/thanks_tests.py
@@ -46,13 +46,7 @@
         site.thank_revision(revid, source='pywikibot test')
         log_entries = site.logevents(logtype='thanks', total=5, page=user,
                                      start=before_time, reverse=True)
-        try:
-            next(iter(log_entries))
-        except StopIteration:
-            found_log = False
-        else:
-            found_log = True
-        self.assertTrue(found_log)
+        self.assertTrue(bool(next(log_entries, None)))

     def test_self_thank(self):
         """Test that thanking oneself causes an error.
diff --git a/tox.ini b/tox.ini
index bfde443..226d907 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,7 +9,7 @@
     hacking-py37

 [params]
-doctest_skip = --ignore-files=(eventstreams|gui)\.py
+doctest_skip = --ignore-files=gui\.py
 generate_user_files = -W error::UserWarning -m pwb generate_user_files 
-family:wikipedia -lang:test -v

 [testenv]
@@ -70,6 +70,7 @@
     nosetests -v --with-doctest pywikibot {[params]doctest_skip}
 deps =
     nose
+    .[eventstreams]
     .[memento]
     .[mwparserfromhell]
     .[mysql]

--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/812372
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.wikimedia.org/r/settings

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: I92941d18ad71f10b04a3c7f7d7bbbd4460e9c861
Gerrit-Change-Number: 812372
Gerrit-PatchSet: 20
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: Matěj Suchánek <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to