jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/656542 )
Change subject: [cleanup] Remove desupported api methods and properties
......................................................................
[cleanup] Remove desupported api methods and properties
remove desupported
- ParamInfo.modules
- ParamInfo.prefixes
- ParamInfo.module_attribute_map()
- ParamInfo.query_modules_with_limits
Change-Id: I6df2d4abd17a10a1fe0bc2d6b29c28ba94af50cd
---
M pywikibot/data/api.py
1 file changed, 2 insertions(+), 62 deletions(-)
Approvals:
JJMC89: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/data/api.py b/pywikibot/data/api.py
index de505d8..bde7b4f 100644
--- a/pywikibot/data/api.py
+++ b/pywikibot/data/api.py
@@ -1,6 +1,6 @@
"""Interface to Mediawiki's api.php."""
#
-# (C) Pywikibot team, 2007-2020
+# (C) Pywikibot team, 2007-2021
#
# Distributed under the terms of the MIT license.
#
@@ -29,7 +29,7 @@
from pywikibot import config, login
-from pywikibot.backports import FrozenSet, Set, Tuple
+from pywikibot.backports import Tuple
from pywikibot.comms import http
from pywikibot.exceptions import (
Server504Error, Server414Error, FatalServerError, NoUsername,
@@ -38,7 +38,6 @@
from pywikibot.family import SubdomainFamily
from pywikibot.login import LoginStatus
from pywikibot.tools import (
- deprecated,
issue_deprecation_warning,
itergroup,
PYTHON_VERSION,
@@ -652,20 +651,6 @@
return param_data[0]
@property
- @deprecated('submodules() or module_paths', since='20150715',
- future_warning=True)
- def modules(self) -> Union[Set[str], FrozenSet[str]]:
- """
- Set of all main and query modules without path prefixes.
-
- Modules with the same names will be only added once (e.g. 'tokens' from
- the action modules and query modules).
-
- @return: module names
- """
- return self.action_modules | self.query_modules
-
- @property
def module_paths(self):
"""Set of all modules using their paths."""
return self._module_set(True)
@@ -714,18 +699,6 @@
return {'{}+{}'.format(prefix, mod) for mod in modules}
@property
- @deprecated('prefix_map', since='20150715', future_warning=True)
- def prefixes(self):
- """
- Mapping of module to its prefix for all modules with a prefix.
-
- This loads paraminfo for all modules.
- """
- if not self._prefixes:
- self._prefixes = self.module_attribute_map('prefix')
- return self._prefixes
-
- @property
def prefix_map(self):
"""
Mapping of module to its prefix for all modules with a prefix.
@@ -758,39 +731,6 @@
return {mod: self[mod][attribute]
for mod in modules if attribute in self[mod]}
- @deprecated('attributes', since='20150715', future_warning=True)
- def module_attribute_map(self, attribute: str,
- modules: Optional[set] = None):
- """
- Mapping of modules with an attribute to the attribute value.
-
- @param attribute: attribute name
- @param modules: modules to include. If None (default) it'll load all
- action and query modules using the module names. It only uses the
- path for query modules which have the same name as an action
- module.
- @rtype: dict using modules as keys
- """
- if modules is None:
- modules = self.modules | self._prefix_submodules(
- self.query_modules & self.action_modules, 'query')
-
- self.fetch(modules)
-
- return {mod: self[mod][attribute]
- for mod in modules if self[mod][attribute]}
-
- @property
- @deprecated('parameter()', since='20150905', future_warning=True)
- def query_modules_with_limits(self):
- """Set of all query modules which have limits."""
- if not self._with_limits:
- self.fetch(self.submodules('query', True))
- self._with_limits = frozenset(
- mod for mod in self.query_modules
- if self.parameter('query+' + mod, 'limit'))
- return self._with_limits
-
class OptionSet(MutableMapping):
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/656542
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: I6df2d4abd17a10a1fe0bc2d6b29c28ba94af50cd
Gerrit-Change-Number: 656542
Gerrit-PatchSet: 3
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: JJMC89 <JJMC89.Wikimedia(a)gmail.com>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/656440 )
Change subject: [bugfix] Ignore failing test_loadimageinfo for Python 3.5
......................................................................
[bugfix] Ignore failing test_loadimageinfo for Python 3.5
loadimageinfo gives a FutureWarning if a result is expected from this
method. Unfortunately it does not work for Python 3.5 and the bot
owner will not be warned. Ignore failing tests with Python 3.5.
Bug: T272157
Change-Id: I5eeb6164e28debeb663242a4f1d658e5182dfd04
---
M tests/site_tests.py
1 file changed, 3 insertions(+), 1 deletion(-)
Approvals:
JJMC89: Looks good to me, approved
jenkins-bot: Verified
diff --git a/tests/site_tests.py b/tests/site_tests.py
index 9a00492..a6a9279 100644
--- a/tests/site_tests.py
+++ b/tests/site_tests.py
@@ -18,7 +18,7 @@
from pywikibot import config
from pywikibot.data import api
from pywikibot.exceptions import HiddenKeyError
-from pywikibot.tools import suppress_warnings
+from pywikibot.tools import PYTHON_VERSION, suppress_warnings
from tests import patch, unittest_print
from tests.aspects import (
@@ -33,6 +33,7 @@
WikidataTestCase,
)
from tests.basepage import BasePageLoadRevisionsCachingTestBase
+from tests.utils import expected_failure_if
class TestSiteObjectDeprecatedFunctions(DefaultSiteTestCase,
@@ -107,6 +108,7 @@
self.assertEqual(self.site.category_namespaces(),
list(self.site.namespace(14, all=True)))
+ @expected_failure_if(PYTHON_VERSION < (3, 6))
def test_loadimageinfo(self):
"""Test deprecation warning if result of loadimageinfo() is used."""
file = pywikibot.FilePage(self.site, 'foo.jpg')
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/656440
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: I5eeb6164e28debeb663242a4f1d658e5182dfd04
Gerrit-Change-Number: 656440
Gerrit-PatchSet: 2
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: JJMC89 <JJMC89.Wikimedia(a)gmail.com>
Gerrit-Reviewer: Mpaa <mpaa.wiki(a)gmail.com>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/655303 )
Change subject: [FEAT] Allow querying alldeletedrevisions
......................................................................
[FEAT] Allow querying alldeletedrevisions
* Implement APISite.alldeletedrevisions()
* Implement User.deleted_contributions()
Change-Id: I669a7bcfde7e8dda6803b3b8858ef14de541439d
---
M pywikibot/page/__init__.py
M pywikibot/site/__init__.py
M tests/site_tests.py
3 files changed, 297 insertions(+), 13 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/page/__init__.py b/pywikibot/page/__init__.py
index b4d259f..c6c8711 100644
--- a/pywikibot/page/__init__.py
+++ b/pywikibot/page/__init__.py
@@ -31,7 +31,7 @@
import pywikibot
-from pywikibot.backports import cache, Dict, List
+from pywikibot.backports import cache, Dict, Iterable, List, Tuple
from pywikibot import config, i18n, textlib
from pywikibot.comms import http
from pywikibot.data.api import APIError
@@ -3255,6 +3255,23 @@
"""
return next(self.contributions(total=1), None)
+ def deleted_contributions(
+ self, *, total: int = 500, **kwargs
+ ) -> Iterable[Tuple[Page, Revision]]:
+ """Yield tuples describing this user's deleted edits.
+
+ @param: total: Limit results to this number of pages
+ @keyword start: Iterate contributions starting at this Timestamp
+ @keyword end: Iterate contributions ending at this Timestamp
+ @keyword reverse: Iterate oldest contributions first (default: newest)
+ @keyword namespaces: Only iterate pages in these namespaces
+ """
+ for data in self.site.alldeletedrevisions(user=self.username,
+ total=total, **kwargs):
+ page = Page(self.site, data['title'], data['ns'])
+ for contrib in data['revisions']:
+ yield page, Revision(**contrib)
+
@deprecate_arg('number', 'total')
def uploadedImages(self, total=10):
"""
diff --git a/pywikibot/site/__init__.py b/pywikibot/site/__init__.py
index 348f34d..60a7ab5 100644
--- a/pywikibot/site/__init__.py
+++ b/pywikibot/site/__init__.py
@@ -28,13 +28,13 @@
from itertools import zip_longest
from pywikibot.login import LoginStatus as _LoginStatus
from textwrap import fill
-from typing import Optional, Union
+from typing import Any, Optional, Union
from warnings import warn
import pywikibot
import pywikibot.family
-from pywikibot.backports import List
+from pywikibot.backports import Dict, List
from pywikibot.comms.http import get_authentication
from pywikibot.data import api
from pywikibot.echo import Notification
@@ -62,6 +62,7 @@
SiteDefinitionError,
SpamblacklistError,
TitleblacklistError,
+ UserRightsError,
UnknownExtension,
)
from pywikibot.site._basesite import BaseSite, PageInUse, RemovedSite
@@ -3136,6 +3137,24 @@
filters)
return wlgen
+ def _check_view_deleted(self, msg_prefix: str, prop: List[str]) -> None:
+ """Check if the user can view deleted comments and content.
+
+ @param msg_prefix: The calling method name
+ @param prop: Requested props to check
+ @raises UserRightsError: user cannot view a requested prop
+ """
+ err = '{}: User:{} not authorized to view '.format(msg_prefix,
+ self.user())
+ if not self.has_right('deletedhistory'):
+ if self.mw_version < '1.34':
+ raise UserRightsError(err + 'deleted revisions.')
+ if 'comment' in prop or 'parsedcomment' in prop:
+ raise UserRightsError(err + 'comments of deleted revisions.')
+ if ('content' in prop and not (self.has_right('deletedtext')
+ or self.has_right('undelete'))):
+ raise UserRightsError(err + 'deleted content.')
+
@deprecated_args(step=None, get_text='content', page='titles',
limit='total')
def deletedrevs(self, titles=None, start=None, end=None,
@@ -3199,16 +3218,7 @@
if start and end:
self.assert_valid_iter_params('deletedrevs', start, end, reverse)
- err = ('deletedrevs: User:{} not authorized to '
- .format(self.user()))
- if not self.has_right('deletedhistory'):
- if self.mw_version < '1.34':
- raise Error(err + 'access deleted revisions.')
- if 'comment' in prop or 'parsedcomment' in prop:
- raise Error(err + 'access comments of deleted revisions.')
- if ('content' in prop and not (self.has_right('deletedtext')
- or self.has_right('undelete'))):
- raise Error(err + 'view deleted content.')
+ self._check_view_deleted('deletedrevs', prop)
revids = kwargs.pop('revids', None)
if not (bool(titles) ^ (revids is not None)):
@@ -3253,6 +3263,63 @@
data['revisions'] = data.pop('deletedrevisions')
yield data
+ @need_version('1.25')
+ def alldeletedrevisions(
+ self,
+ *,
+ namespaces=None,
+ reverse: bool = False,
+ content: bool = False,
+ total: Optional[int] = None,
+ **kwargs
+ ) -> typing.Iterable[Dict[str, Any]]:
+ """
+ Iterate all deleted revisions.
+
+ @see: U{https://www.mediawiki.org/wiki/API:Alldeletedrevisions}
+
+ @param namespaces: Only iterate pages in these namespaces
+ @type namespaces: iterable of str or Namespace key,
+ or a single instance of those types. May be a '|' separated
+ list of namespace identifiers.
+ @param reverse: Iterate oldest revisions first (default: newest)
+ @param content: If True, retrieve the content of each revision
+ @param total: Number of revisions to retrieve
+ @keyword from: Start listing at this title
+ @keyword to: Stop listing at this title
+ @keyword prefix: Search for all page titles that begin with this value
+ @keyword excludeuser: Exclude revisions by this user
+ @keyword tag: Only list revisions tagged with this tag
+ @keyword user: List revisions by this user
+ @keyword start: Iterate revisions starting at this Timestamp
+ @keyword end: Iterate revisions ending at this Timestamp
+ @keyword prop: Which properties to get. Defaults are ids, timestamp,
+ flags, user, and comment (if you have the right to view).
+ @type prop: List[str]
+ """
+ if 'start' in kwargs and 'end' in kwargs:
+ self.assert_valid_iter_params('alldeletedrevisions',
+ kwargs['start'],
+ kwargs['end'],
+ reverse)
+ prop = kwargs.pop('prop') or []
+ parameters = {'adr' + k: v for k, v in kwargs.items()}
+ if not prop:
+ prop = ['ids', 'timestamp', 'flags', 'user']
+ if self.has_right('deletedhistory'):
+ prop.append('comment')
+ if content:
+ prop.append('content')
+ self._check_view_deleted('alldeletedrevisions', prop)
+ parameters['adrprop'] = prop
+ if reverse:
+ parameters['adrdir'] = 'newer'
+ yield from self._generator(api.ListGenerator,
+ type_arg='alldeletedrevisions',
+ namespaces=namespaces,
+ total=total,
+ parameters=parameters)
+
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 e3469e5..0f082bf 100644
--- a/tests/site_tests.py
+++ b/tests/site_tests.py
@@ -1624,6 +1624,144 @@
end='2008-10-03T00:00:01Z', reverse=True, total=5)
+class TestAlldeletedrevisionsAsUser(DefaultSiteTestCase):
+
+ """Test site method site.alldeletedrevisions() with bot user."""
+
+ user = True
+
+ def test_basic(self):
+ """Test the site.alldeletedrevisions() method."""
+ mysite = self.get_site()
+ drev = list(mysite.alldeletedrevisions(user=mysite.user(), total=10))
+ self.assertTrue(all(isinstance(data, dict)
+ for data in drev))
+ self.assertTrue(all('revisions' in data
+ and isinstance(data['revisions'], dict)
+ for data in drev))
+ self.assertTrue(all('user' in rev
+ and rev['user'] == mysite.user()
+ for data in drev
+ for rev in data))
+
+ def test_namespaces(self):
+ """Test the site.alldeletedrevisions() method using namespaces."""
+ mysite = self.get_site()
+ for data in mysite.alldeletedrevisions(namespaces=14, total=5):
+ self.assertIsInstance(data, dict)
+ self.assertIn('title', data)
+ self.assertTrue(data['title'].startswith(mysite.namespace(14)))
+
+ for data in mysite.alldeletedrevisions(user=mysite.user(),
+ namespaces=[10, 11],
+ total=5):
+ self.assertIsInstance(data, dict)
+ self.assertIn('title', data)
+ self.assertIn(data['ns'], (10, 11))
+
+ def test_excludeuser(self):
+ """Test the site.alldeletedrevisions() method using excludeuser."""
+ mysite = self.get_site()
+ for data in mysite.alldeletedrevisions(excludeuser=mysite.user(),
+ total=5):
+ self.assertIsInstance(data, dict)
+ for drev in data:
+ self.assertIsInstance(drev, dict)
+ self.assertIn('user', data)
+ self.assertNotEqual('user', mysite.user())
+
+ def test_user_range(self):
+ """Test the site.alldeletedrevisions() method with range."""
+ mysite = self.get_site()
+ start = '2008-10-06T01:02:03Z'
+ for data in mysite.alldeletedrevisions(
+ user=mysite.user(),
+ start=pywikibot.Timestamp.fromISOformat(start),
+ total=5):
+ for drev in data:
+ self.assertLessEqual(drev['timestamp'], start)
+
+ end = '2008-10-07T02:03:04Z'
+ for data in mysite.alldeletedrevisions(
+ user=mysite.user(),
+ end=pywikibot.Timestamp.fromISOformat(end),
+ total=5):
+ for drev in data:
+ self.assertGreaterEqual(drev['timestamp'], end)
+
+ start = '2008-10-10T11:59:59Z'
+ end = '2008-10-10T00:00:01Z'
+ for data in mysite.alldeletedrevisions(
+ user=mysite.user(),
+ start=pywikibot.Timestamp.fromISOformat(start),
+ end=pywikibot.Timestamp.fromISOformat(end),
+ total=5):
+ for drev in data:
+ self.assertTrue(end <= drev['timestamp'] <= start)
+
+ def test_user_range_reverse(self):
+ """Test the site.alldeletedrevisions() method with range reversed."""
+ mysite = self.get_site()
+ start = '2008-10-08T03:05:07Z'
+ for data in mysite.alldeletedrevisions(
+ user=mysite.user(),
+ start=pywikibot.Timestamp.fromISOformat(start),
+ total=5, reverse=True):
+ for drev in data:
+ self.assertGreaterEqual(drev['timestamp'], start)
+
+ for data in mysite.alldeletedrevisions(
+ user=mysite.user(),
+ end=pywikibot.Timestamp.fromISOformat('2008-10-09T04:06:08Z'),
+ total=5, reverse=True):
+ for drev in data:
+ self.assertLessEqual(drev['timestamp'], '2008-10-09T04:06:08Z')
+
+ start = '2008-10-11T06:00:01Z'
+ end = '2008-10-11T23:59:59Z'
+ for data in mysite.alldeletedrevisions(
+ user=mysite.user(),
+ start=pywikibot.Timestamp.fromISOformat(start),
+ end=pywikibot.Timestamp.fromISOformat(end),
+ reverse=True, total=5):
+ for drev in data:
+ self.assertTrue(start <= drev['timestamp'] <= end)
+
+ def test_invalid_range(self):
+ """Test site.alldeletedrevisions() method with invalid range."""
+ mysite = self.get_site()
+ # start earlier than end
+ self.assertRaises(AssertionError, mysite.alldeletedrevisions,
+ user=mysite.user(),
+ start='2008-10-03T00:00:01Z',
+ end='2008-10-03T23:59:59Z', total=5)
+ # reverse: end earlier than start
+ self.assertRaises(AssertionError, mysite.alldeletedrevisions,
+ user=mysite.user(),
+ start='2008-10-03T23:59:59Z',
+ end='2008-10-03T00:00:01Z', reverse=True, total=5)
+
+
+class TestAlldeletedrevisionsWithoutUser(DefaultSiteTestCase):
+
+ """Test site method site.alldeletedrevisions() without bot user."""
+
+ def test_prefix(self):
+ """Test the site.alldeletedrevisions() method with prefix."""
+ mysite = self.get_site()
+ for data in mysite.alldeletedrevisions(prefix='John', total=5):
+ self.assertIsInstance(data, dict)
+ for key in ('title', 'ns'):
+ self.assertIn(key, data)
+ title = data['title']
+ if data['ns'] > 0:
+ *_, title = title.partition(':')
+ self.assertTrue(title.startswith('John'))
+ for drev in data['revisions']:
+ for key in ('parentid', 'revid', 'timestamp', 'user'):
+ self.assertIn(key, drev)
+
+
class SiteWatchlistRevsTestCase(DefaultSiteTestCase):
"""Test site method watchlist_revs()."""
@@ -1817,6 +1955,68 @@
reverse=True)
next(gen)
+ def test_alldeletedrevisions(self):
+ """Test the site.alldeletedrevisions() method."""
+ mysite = self.get_site()
+ myuser = mysite.user()
+ if not mysite.has_right('deletedhistory'):
+ self.skipTest(
+ "You don't have permission to view the deleted revisions "
+ 'on {0}.'.format(mysite))
+ prop = ['ids', 'timestamp', 'flags', 'user', 'comment']
+ gen = mysite.alldeletedrevisions(total=10, prop=prop)
+
+ for item in gen:
+ break
+ else:
+ self.skipTest('{0} does not have deleted edits.'.format(myuser))
+ self.assertIn('revisions', item)
+ for drev in item['revisions']:
+ for key in ('parentid', 'revid', 'timestamp', 'user', 'comment'):
+ self.assertIn(key, drev)
+
+ with self.subTest(start='2008-10-11T01:02:03Z', reverse=False,
+ prop=prop):
+ for item in mysite.alldeletedrevisions(
+ start='2008-10-11T01:02:03Z',
+ total=5
+ ):
+ for drev in item['revisions']:
+ self.assertIsInstance(drev, dict)
+ self.assertLessEqual(drev['timestamp'],
+ '2008-10-11T01:02:03Z')
+
+ with self.subTest(start='2008-10-11T01:02:03Z', reverse=True,
+ prop=prop):
+ for item in mysite.alldeletedrevisions(
+ start='2008-10-11T01:02:03Z',
+ total=5
+ ):
+ for drev in item['revisions']:
+ self.assertIsInstance(drev, dict)
+ self.assertGreaterEqual(drev['timestamp'],
+ '2008-10-11T01:02:03Z')
+
+ # start earlier than end
+ with self.subTest(start='2008-09-03T00:00:01Z',
+ end='2008-09-03T23:59:59Z',
+ reverse=False, prop=prop):
+ with self.assertRaises(AssertionError):
+ gen = mysite.alldeletedrevisions(start='2008-09-03T00:00:01Z',
+ end='2008-09-03T23:59:59Z',
+ total=5)
+ next(gen)
+
+ # reverse: end earlier than start
+ with self.subTest(start='2008-09-03T23:59:59Z',
+ end='2008-09-03T00:00:01Z',
+ reverse=True, prop=prop):
+ with self.assertRaises(AssertionError):
+ gen = mysite.alldeletedrevisions(start='2008-09-03T23:59:59Z',
+ end='2008-09-03T00:00:01Z',
+ total=5, reverse=True)
+ next(gen)
+
class TestSiteSysopWrite(TestCase):
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/655303
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: I669a7bcfde7e8dda6803b3b8858ef14de541439d
Gerrit-Change-Number: 655303
Gerrit-PatchSet: 10
Gerrit-Owner: JJMC89 <JJMC89.Wikimedia(a)gmail.com>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-CC: DannyS712 <DannyS712.enwiki(a)gmail.com>
Gerrit-MessageType: merged