jenkins-bot merged this change.

View Change

Approvals: Huji: Looks good to me, approved jenkins-bot: Verified
[bugfix] Implement deletedrevisions api call

deletedrevs is deprecated and might be removed.

site changes:
- With mw 1.25 a 'deletedrevisions' PropertyGenerator is used
to get deleted revisions.
- All API parameters can be used as keyword options
- The revids parameter is available too.
- page was renamed to titles. Both titles and revids are allowed to
be an iterable instead of a single item
- All properties can be set with prop parameter if others than the default
properties should be shown. If the content of a revision is requested,
this will be added to the given properties or added to the default.
- Fallback to 'deletedrevs' ListGenerator for mw_version prior than 1.25
- Take into account that the two request types have different dicts

Test changes:
- Replace "page" with "titles"
- Use subTest to separate tests inside test method
- The generator must be started to validate the AssertionError
since we have a real generator instead an iterable

Bug: T75370
Change-Id: I4c47abbc9ba0fcb03c20bcd4486020ac829a5b2b
---
M pywikibot/site.py
M tests/site_tests.py
2 files changed, 182 insertions(+), 91 deletions(-)

diff --git a/pywikibot/site.py b/pywikibot/site.py
index a55a66c..d1b6518 100644
--- a/pywikibot/site.py
+++ b/pywikibot/site.py
@@ -5015,69 +5015,129 @@
filters)
return wlgen

- # TODO: T75370
- @deprecated_args(step=None, get_text='content')
- def deletedrevs(self, page, start=None, end=None, reverse=False,
- content=False, total=None):
+ @deprecated_args(step=None, get_text='content', page='titles',
+ limit='total')
+ def deletedrevs(self, titles=None, start=None, end=None, reverse=False,
+ content=False, total=None, **kwargs):
"""Iterate deleted revisions.

Each value returned by the iterator will be a dict containing the
'title' and 'ns' keys for a particular Page and a 'revisions' key
whose value is a list of revisions in the same format as
- recentchanges (plus a 'content' element if requested). If get_text
- is true, the toplevel dict will contain a 'token' key as well.
+ recentchanges plus a 'content' element with key '*' if requested
+ when 'content' parameter is set. For older wikis a 'token' key is
+ also given with the content request.

- @see: U{https://www.mediawiki.org/wiki/API:Deletedrevs}
+ @see: U{https://www.mediawiki.org/wiki/API:Deletedrevisions}

- @param page: The page to check for deleted revisions
+ @param titles: The page titles to check for deleted revisions
+ @type titles: str (multiple titles delimited with '|')
+ or pywikibot.Page or typing.Iterable[pywikibot.Page]
+ or typing.Iterable[str]
+ @keyword revids: Get revisions by their ID
+
+ @note either titles or revids must be set but not both
+
@param start: Iterate revisions starting at this Timestamp
@param end: Iterate revisions ending at this Timestamp
@param reverse: Iterate oldest revisions first (default: newest)
@type reverse: bool
- @param content: If True, retrieve the content of each revision and
- an undelete token
+ @param content: If True, retrieve the content of each revision
+ @param total: number of revisions to retrieve
+ @keyword user: List revisions by this user
+ @keyword excludeuser: Exclude revisions by this user
+ @keyword tag: Only list revision tagged with this tag
+ @keyword prop: Which properties to get. Defaults are ids, user,
+ comment, flags and timestamp
"""
+ def handle_props(props):
+ """Translate deletedrev props to deletedrevisions props."""
+ if isinstance(props, UnicodeType):
+ props = props.split('|')
+ if self.mw_version >= '1.25':
+ return props
+
+ old_props = []
+ for item in props:
+ if item == 'ids':
+ old_props += ['revid', 'parentid']
+ elif item == 'flags':
+ old_props.append('minor')
+ elif item == 'timestamp':
+ pass
+ else:
+ old_props.append(item)
+ if item == 'content' and self.mw_version < '1.24':
+ old_props.append('token')
+ return old_props
+
if start and end:
self.assert_valid_iter_params('deletedrevs', start, end, reverse)

if not self.logged_in():
self.login()
+
+ err = ('deletedrevs: User:{} not authorized to '
+ .format(self.user()))
if 'deletedhistory' not in self.userinfo['rights']:
- try:
- self.login(True)
- except NoUsername:
- pass
- if 'deletedhistory' not in self.userinfo['rights']:
- raise Error(
- 'deletedrevs: '
- 'User:%s not authorized to access deleted revisions.'
- % self.user())
+ raise Error(err + 'access deleted revisions.')
if content:
if 'undelete' not in self.userinfo['rights']:
- try:
- self.login(True)
- except NoUsername:
- pass
- if 'undelete' not in self.userinfo['rights']:
- raise Error(
- 'deletedrevs: '
- 'User:%s not authorized to view deleted content.'
- % self.user())
+ raise Error(err + 'view deleted content.')

- drgen = self._generator(api.ListGenerator, type_arg='deletedrevs',
- titles=page.title(with_section=False),
- drprop='revid|user|comment|minor',
- total=total)
+ revids = kwargs.pop('revids', None)
+ if not (bool(titles) ^ (revids is not None)):
+ raise Error('deletedrevs: either "titles" or "revids" parameter '
+ 'must be given.')
+ if revids and self.mw_version < '1.25':
+ raise NotImplementedError(
+ 'deletedrevs: "revid" is not implemented with MediaWiki {}'
+ .format(self.mw_version))
+
+ if self.mw_version >= '1.25':
+ pre = 'drv'
+ type_arg = 'deletedrevisions'
+ generator = api.PropertyGenerator
+ else:
+ pre = 'dr'
+ type_arg = 'deletedrevs'
+ generator = api.ListGenerator
+
+ gen = self._generator(generator, type_arg=type_arg,
+ titles=titles, revids=revids,
+ total=total)
+
+ gen.request[pre + 'start'] = start
+ gen.request[pre + 'end'] = end
+
+ # handle properties
+ prop = kwargs.pop('prop',
+ ['ids', 'user', 'comment', 'flags', 'timestamp'])
if content:
- drgen.request['drprop'] = (drgen.request['drprop']
- + ['content', 'token'])
- if start is not None:
- drgen.request['drstart'] = start
- if end is not None:
- drgen.request['drend'] = end
+ prop.append('content')
+ prop = handle_props(prop)
+ gen.request[pre + 'prop'] = prop
+
+ # handle other parameters like user
+ for k, v in kwargs.items():
+ gen.request[pre + k] = v
+
if reverse:
- drgen.request['drdir'] = 'newer'
- return drgen
+ gen.request[pre + 'dir'] = 'newer'
+
+ if self.mw_version < '1.25':
+ # yield from gen
+ for data in gen:
+ yield data
+ else:
+ # The dict result is different for both generators
+ for data in gen:
+ try:
+ data['revisions'] = data.pop('deletedrevisions')
+ except KeyError:
+ pass
+ else:
+ yield data

def users(self, usernames):
"""Iterate info about a list of users by name or IP.
diff --git a/tests/site_tests.py b/tests/site_tests.py
index d4bf58c..4c84f3b 100644
--- a/tests/site_tests.py
+++ b/tests/site_tests.py
@@ -1885,66 +1885,97 @@
"""Test the site.deletedrevs() method."""
mysite = self.get_site()
mainpage = self.get_mainpage()
- gen = mysite.deletedrevs(total=10, page=mainpage)
+ gen = mysite.deletedrevs(total=10, titles=mainpage)
+
for dr in gen:
break
else:
self.skipTest(
'{0} contains no deleted revisions.'.format(mainpage))
self.assertLessEqual(len(dr['revisions']), 10)
- self.assertTrue(all(isinstance(rev, dict)
- for rev in dr['revisions']))
- for item in mysite.deletedrevs(start='2008-10-11T01:02:03Z',
- page=mainpage, total=5):
- for rev in item['revisions']:
- self.assertIsInstance(rev, dict)
- self.assertLessEqual(rev['timestamp'], '2008-10-11T01:02:03Z')
- for item in mysite.deletedrevs(end='2008-04-01T02:03:04Z',
- page=mainpage, total=5):
- for rev in item['revisions']:
- self.assertIsInstance(rev, dict)
- self.assertGreaterEqual(rev['timestamp'],
- '2008-10-11T02:03:04Z')
- for item in mysite.deletedrevs(start='2008-10-11T03:05:07Z',
- page=mainpage, total=5,
- reverse=True):
- for rev in item['revisions']:
- self.assertIsInstance(rev, dict)
- self.assertGreaterEqual(rev['timestamp'],
- '2008-10-11T03:05:07Z')
- for item in mysite.deletedrevs(end='2008-10-11T04:06:08Z',
- page=mainpage, total=5,
- reverse=True):
- for rev in item['revisions']:
- self.assertIsInstance(rev, dict)
- self.assertLessEqual(rev['timestamp'], '2008-10-11T04:06:08Z')
- for item in mysite.deletedrevs(start='2008-10-13T11:59:59Z',
- end='2008-10-13T00:00:01Z',
- page=mainpage, total=5):
- for rev in item['revisions']:
- self.assertIsInstance(rev, dict)
- self.assertLessEqual(rev['timestamp'], '2008-10-13T11:59:59Z')
- self.assertGreaterEqual(rev['timestamp'],
- '2008-10-13T00:00:01Z')
- for item in mysite.deletedrevs(start='2008-10-15T06:00:01Z',
- end='2008-10-15T23:59:59Z',
- page=mainpage, reverse=True,
- total=5):
- for rev in item['revisions']:
- self.assertIsInstance(rev, dict)
- self.assertLessEqual(rev['timestamp'], '2008-10-15T23:59:59Z')
- self.assertGreaterEqual(rev['timestamp'],
- '2008-10-15T06:00:01Z')
+ self.assertTrue(all(isinstance(rev, dict) for rev in dr['revisions']))
+
+ with self.subTest(start='2008-10-11T01:02:03Z', reverse=False):
+ for item in mysite.deletedrevs(start='2008-10-11T01:02:03Z',
+ titles=mainpage, total=5):
+ for rev in item['revisions']:
+ self.assertIsInstance(rev, dict)
+ self.assertLessEqual(rev['timestamp'],
+ '2008-10-11T01:02:03Z')
+
+ with self.subTest(end='2008-04-01T02:03:04Z', reverse=False):
+ for item in mysite.deletedrevs(end='2008-04-01T02:03:04Z',
+ titles=mainpage, total=5):
+ for rev in item['revisions']:
+ self.assertIsInstance(rev, dict)
+ self.assertGreaterEqual(rev['timestamp'],
+ '2008-10-11T02:03:04Z')
+
+ with self.subTest(start='2008-10-11T03:05:07Z', reverse=True):
+ for item in mysite.deletedrevs(start='2008-10-11T03:05:07Z',
+ titles=mainpage, total=5,
+ reverse=True):
+ for rev in item['revisions']:
+ self.assertIsInstance(rev, dict)
+ self.assertGreaterEqual(rev['timestamp'],
+ '2008-10-11T03:05:07Z')
+
+ with self.subTest(end='2008-10-11T04:06:08Z', reverse=True):
+ for item in mysite.deletedrevs(end='2008-10-11T04:06:08Z',
+ titles=mainpage, total=5,
+ reverse=True):
+ for rev in item['revisions']:
+ self.assertIsInstance(rev, dict)
+ self.assertLessEqual(rev['timestamp'],
+ '2008-10-11T04:06:08Z')
+
+ with self.subTest(start='2008-10-13T11:59:59Z',
+ end='2008-10-13T00:00:01Z',
+ reverse=False):
+ for item in mysite.deletedrevs(start='2008-10-13T11:59:59Z',
+ end='2008-10-13T00:00:01Z',
+ titles=mainpage, total=5):
+ for rev in item['revisions']:
+ self.assertIsInstance(rev, dict)
+ self.assertLessEqual(rev['timestamp'],
+ '2008-10-13T11:59:59Z')
+ self.assertGreaterEqual(rev['timestamp'],
+ '2008-10-13T00:00:01Z')
+
+ with self.subTest(start='2008-10-15T06:00:01Z',
+ end='2008-10-15T23:59:59Z',
+ reverse=True):
+ for item in mysite.deletedrevs(start='2008-10-15T06:00:01Z',
+ end='2008-10-15T23:59:59Z',
+ titles=mainpage, total=5,
+ reverse=True):
+ for rev in item['revisions']:
+ self.assertIsInstance(rev, dict)
+ self.assertLessEqual(rev['timestamp'],
+ '2008-10-15T23:59:59Z')
+ self.assertGreaterEqual(rev['timestamp'],
+ '2008-10-15T06:00:01Z')

# start earlier than end
- self.assertRaises(AssertionError, mysite.deletedrevs,
- page=mainpage, start='2008-09-03T00:00:01Z',
- end='2008-09-03T23:59:59Z', total=5)
+ with self.subTest(start='2008-09-03T00:00:01Z',
+ end='2008-09-03T23:59:59Z',
+ reverse=False):
+ with self.assertRaises(AssertionError):
+ gen = mysite.deletedrevs(titles=mainpage,
+ start='2008-09-03T00:00:01Z',
+ end='2008-09-03T23:59:59Z', total=5)
+ next(gen)
+
# reverse: end earlier than start
- self.assertRaises(AssertionError, mysite.deletedrevs,
- page=mainpage, start='2008-09-03T23:59:59Z',
- end='2008-09-03T00:00:01Z', reverse=True,
- total=5)
+ with self.subTest(start='2008-09-03T23:59:59Z',
+ end='2008-09-03T00:00:01Z',
+ reverse=True):
+ with self.assertRaises(AssertionError):
+ gen = mysite.deletedrevs(titles=mainpage,
+ start='2008-09-03T23:59:59Z',
+ end='2008-09-03T00:00:01Z', total=5,
+ reverse=True)
+ next(gen)


class TestSiteSysopWrite(TestCase):

To view, visit change 538006. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: I4c47abbc9ba0fcb03c20bcd4486020ac829a5b2b
Gerrit-Change-Number: 538006
Gerrit-PatchSet: 16
Gerrit-Owner: Xqt <info@gno.de>
Gerrit-Reviewer: Huji <huji.huji@gmail.com>
Gerrit-Reviewer: Xqt <info@gno.de>
Gerrit-Reviewer: jenkins-bot (75)