jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/816302 )
Change subject: [IMPR] Make GoogleSearchPageGenerator.queryGoogle a staticmethod
......................................................................
[IMPR] Make GoogleSearchPageGenerator.queryGoogle a staticmethod
Change-Id: I15889ced77f856942e2d0bf5093c2fde2ae596de
---
M pywikibot/pagegenerators/_generators.py
1 file changed, 3 insertions(+), 3 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/pagegenerators/_generators.py b/pywikibot/pagegenerators/_generators.py
index f45ba45..d8bd16e 100644
--- a/pywikibot/pagegenerators/_generators.py
+++ b/pywikibot/pagegenerators/_generators.py
@@ -808,9 +808,9 @@
self.site = site
self._google_query = None
- def queryGoogle(self, query: str) -> Iterator[Any]:
- """
- Perform a query using python package 'google'.
+ @staticmethod
+ def queryGoogle(query: str) -> Iterator[Any]:
+ """Perform a query using python package 'google'.
The terms of service as at June 2014 give two conditions that
may apply to use of search:
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/816302
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: I15889ced77f856942e2d0bf5093c2fde2ae596de
Gerrit-Change-Number: 816302
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
Xqt has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/816297 )
Change subject: [CI] ignore some code from coverage
......................................................................
[CI] ignore some code from coverage
Change-Id: Ic485c6677bdb27903b280a68160ff4386a284186
---
M pywikibot/bot.py
M pywikibot/bot_choice.py
M pywikibot/page/_pages.py
3 files changed, 6 insertions(+), 6 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Verified; Looks good to me, approved
diff --git a/pywikibot/bot.py b/pywikibot/bot.py
index 19a3f24..97948ea 100644
--- a/pywikibot/bot.py
+++ b/pywikibot/bot.py
@@ -415,7 +415,7 @@
# give up infinite rotating file handler with logfilecount of -1;
# set it to 999 and use the standard implementation
max_count = config.logfilecount
- if max_count == -1:
+ if max_count == -1: # pragma: no cover
max_count = 999
issue_deprecation_warning('config.logfilecount with value -1',
'any positive number',
@@ -2036,7 +2036,7 @@
return super().skip_page(page)
-class RedirectPageBot(CurrentPageBot):
+class RedirectPageBot(CurrentPageBot): # pragma: no cover
"""A RedirectPageBot class which only treats redirects.
@@ -2062,7 +2062,7 @@
return super().skip_page(page)
-class NoRedirectPageBot(CurrentPageBot):
+class NoRedirectPageBot(CurrentPageBot): # pragma: no cover
"""A NoRedirectPageBot class which only treats non-redirects.
diff --git a/pywikibot/bot_choice.py b/pywikibot/bot_choice.py
index 040f06f..6e13acf 100644
--- a/pywikibot/bot_choice.py
+++ b/pywikibot/bot_choice.py
@@ -195,7 +195,7 @@
@property
def out(self) -> str:
"""Return the contents."""
- if not hasattr(self._outputter, 'out'):
+ if not hasattr(self._outputter, 'out'): # pragma: no cover
issue_deprecation_warning('{} without "out" property'
.format(self.__class__.__name__),
since='6.2.0')
diff --git a/pywikibot/page/_pages.py b/pywikibot/page/_pages.py
index a6f4ea3..0d4f2be 100644
--- a/pywikibot/page/_pages.py
+++ b/pywikibot/page/_pages.py
@@ -1223,7 +1223,7 @@
if not summary:
summary = config.default_edit_summary
- if isinstance(watch, bool):
+ if isinstance(watch, bool): # pragma: no cover
issue_deprecation_warning(
'boolean watch parameter',
'"watch", "unwatch", "preferences" or "nochange" value',
@@ -1420,7 +1420,7 @@
"""
# Deprecate positional arguments and synchronize with Site.pagelinks
keys = ('namespaces', 'total', 'content')
- for i, arg in enumerate(args):
+ for i, arg in enumerate(args): # pragma: no cover
key = keys[i]
issue_deprecation_warning(
'Positional argument {} ({})'.format(i + 1, arg),
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/816297
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: Ic485c6677bdb27903b280a68160ff4386a284186
Gerrit-Change-Number: 816297
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
Xqt has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/816298 )
Change subject: [cleanup] Remove TODO: implement undelete in site/_generators.py
......................................................................
[cleanup] Remove TODO: implement undelete in site/_generators.py
The TODO was added February 2009 but the undelete method was
implemented in December 2014
Change-Id: Ibefcd4a2253036df7adaaf490fedd89a5488a412
---
M pywikibot/site/_generators.py
1 file changed, 0 insertions(+), 2 deletions(-)
Approvals:
Xqt: Verified; Looks good to me, approved
diff --git a/pywikibot/site/_generators.py b/pywikibot/site/_generators.py
index 6d9966a..7b1259f 100644
--- a/pywikibot/site/_generators.py
+++ b/pywikibot/site/_generators.py
@@ -1748,8 +1748,6 @@
namespaces=namespaces, total=total,
g_content=content, **params)
- # TODO: implement undelete
-
_patrol_errors = {
'nosuchrcid': 'There is no change with rcid {rcid}',
'nosuchrevid': 'There is no change with revid {revid}',
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/816298
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: Ibefcd4a2253036df7adaaf490fedd89a5488a412
Gerrit-Change-Number: 816298
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-MessageType: merged
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/812560 )
Change subject: [IMPR] Show additional parser error messages with DataSite.parsevalue()
......................................................................
[IMPR] Show additional parser error messages with DataSite.parsevalue()
Change-Id: I3c5897d16670fad62bfdc6711cbb1be3b1ce217b
---
M pywikibot/site/_datasite.py
1 file changed, 12 insertions(+), 5 deletions(-)
Approvals:
Matěj Suchánek: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/site/_datasite.py b/pywikibot/site/_datasite.py
index 62b9307..3b16ac1 100644
--- a/pywikibot/site/_datasite.py
+++ b/pywikibot/site/_datasite.py
@@ -745,18 +745,25 @@
try:
data = req.submit()
except APIError as e:
- if e.code == 'wikibase-parse-error-quantity':
+ if e.code.startswith('wikibase-parse-error'):
+ for err in e.other['results']:
+ if 'error' in err:
+ pywikibot.error('{error-info} for value {raw!r}, '
+ '{expected-format!r} format expected'
+ .format_map(err))
raise ValueError(e) from None
raise
+
if 'results' not in data:
- raise ValueError('Parsing via wikibase wbparsevalue failed.')
+ raise RuntimeError("Unexpected missing 'results' in query data\n{}"
+ .format(data))
results = []
for result_hash in data['results']:
if 'value' not in result_hash:
- raise ValueError(
- 'Parsing via wikibase wbparsevalue failed: {}'
- .format(result_hash['error-info']))
+ # There should be an APIError occurred already
+ raise RuntimeError("Unexpected missing 'value' in query data:"
+ '\n{}'.format(result_hash))
results.append(result_hash['value'])
return results
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/812560
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: I3c5897d16670fad62bfdc6711cbb1be3b1ce217b
Gerrit-Change-Number: 812560
Gerrit-PatchSet: 4
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Matěj Suchánek <matejsuchanek97(a)gmail.com>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
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 <info(a)gno.de>
Gerrit-Reviewer: Matěj Suchánek <matejsuchanek97(a)gmail.com>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/816259 )
Change subject: [doc] Update pagegenerators doc
......................................................................
[doc] Update pagegenerators doc
Change-Id: I8e5adc57dc3d243d0c417ab723ae89468b04e2a5
---
A docs/api_ref/pywikibot.pagegenerators.rst
M docs/api_ref/pywikibot.rst
M pywikibot/pagegenerators/_factory.py
M pywikibot/pagegenerators/_filters.py
M pywikibot/pagegenerators/_generators.py
5 files changed, 24 insertions(+), 47 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/docs/api_ref/pywikibot.pagegenerators.rst b/docs/api_ref/pywikibot.pagegenerators.rst
new file mode 100644
index 0000000..5a48ff1
--- /dev/null
+++ b/docs/api_ref/pywikibot.pagegenerators.rst
@@ -0,0 +1,20 @@
+pagegenerators package
+======================
+
+.. automodule:: pywikibot.pagegenerators
+
+pagegenerators.\_factory module
+-------------------------------
+
+.. automodule:: pywikibot.pagegenerators._factory
+
+pagegenerators.\_filters module
+-------------------------------
+
+.. automodule:: pywikibot.pagegenerators._filters
+
+pagegenerators.\_generators module
+----------------------------------
+
+.. automodule:: pywikibot.pagegenerators._generators
+
diff --git a/docs/api_ref/pywikibot.rst b/docs/api_ref/pywikibot.rst
index 76d1495..3e0e3b6 100644
--- a/docs/api_ref/pywikibot.rst
+++ b/docs/api_ref/pywikibot.rst
@@ -12,6 +12,7 @@
pywikibot.data
pywikibot.families
pywikibot.page
+ pywikibot.pagegenerators
pywikibot.site
pywikibot.specialbots
pywikibot.tools
@@ -114,11 +115,6 @@
.. automodule:: pywikibot.login
-pywikibot.pagegenerators module
--------------------------------
-
-.. automodule:: pywikibot.pagegenerators
-
pywikibot.plural module
-----------------------
diff --git a/pywikibot/pagegenerators/_factory.py b/pywikibot/pagegenerators/_factory.py
index 7c5e268..61ca0d3 100644
--- a/pywikibot/pagegenerators/_factory.py
+++ b/pywikibot/pagegenerators/_factory.py
@@ -1,17 +1,4 @@
-"""
-This module offers a wide variety of page generators.
-
-A page generator is an object that is iterable (see :pep:`255`) and
-that yields page objects on which other scripts can then work.
-
-Most of these functions just wrap a Site or Page method that returns a
-generator. For testing purposes listpages.py can be used, to print page
-titles to standard output.
-
-These parameters are supported to specify which pages titles to print:
-
-¶ms;
-"""
+"""GeneratorFactory module wich handles pagegenerators options."""
#
# (C) Pywikibot team, 2008-2022
#
diff --git a/pywikibot/pagegenerators/_filters.py b/pywikibot/pagegenerators/_filters.py
index 9e471d1..bf12b34 100644
--- a/pywikibot/pagegenerators/_filters.py
+++ b/pywikibot/pagegenerators/_filters.py
@@ -1,17 +1,4 @@
-"""
-This module offers a wide variety of page generators.
-
-A page generator is an object that is iterable (see :pep:`255`) and
-that yields page objects on which other scripts can then work.
-
-Most of these functions just wrap a Site or Page method that returns a
-generator. For testing purposes listpages.py can be used, to print page
-titles to standard output.
-
-These parameters are supported to specify which pages titles to print:
-
-¶ms;
-"""
+"""Page filter generators provided by the pagegenerators module."""
#
# (C) Pywikibot team, 2008-2022
#
diff --git a/pywikibot/pagegenerators/_generators.py b/pywikibot/pagegenerators/_generators.py
index 7505b33..f45ba45 100644
--- a/pywikibot/pagegenerators/_generators.py
+++ b/pywikibot/pagegenerators/_generators.py
@@ -1,17 +1,4 @@
-"""
-This module offers a wide variety of page generators.
-
-A page generator is an object that is iterable (see :pep:`255`) and
-that yields page objects on which other scripts can then work.
-
-Most of these functions just wrap a Site or Page method that returns a
-generator. For testing purposes listpages.py can be used, to print page
-titles to standard output.
-
-These parameters are supported to specify which pages titles to print:
-
-¶ms;
-"""
+"""Page filter generators provided by the pagegenerators module."""
#
# (C) Pywikibot team, 2008-2022
#
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/816259
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: I8e5adc57dc3d243d0c417ab723ae89468b04e2a5
Gerrit-Change-Number: 816259
Gerrit-PatchSet: 3
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged