jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1325915?usp=email )
Change subject: api: Standardize page-action summary parameters
......................................................................
api: Standardize page-action summary parameters
Use summary for public page move, delete, undelete, and protect
messages, and for upload edit summaries.
Keep reason and comment as deprecated aliases while preserving
MediaWiki wire names. Leave review comments and unrelated
administrative reasons unchanged.
Update bundled callers, documentation, and regression coverage.
Bug: T62442
Change-Id: I3384d6d97121abc9f7729c0ada7c22ccf4c98e59
---
M ROADMAP.rst
M pywikibot/page/_basepage.py
M pywikibot/page/_filepage.py
M pywikibot/site/_apisite.py
M pywikibot/site/_upload.py
M pywikibot/specialbots/_upload.py
M scripts/category.py
M scripts/delete.py
M scripts/imagetransfer.py
M scripts/movepages.py
M scripts/nowcommons.py
M scripts/protect.py
M scripts/redirect.py
M scripts/speedy_delete.py
M tests/deletionbot_tests.py
M tests/flaggedrevs_tests.py
M tests/page_tests.py
M tests/site_tests.py
M tests/upload_tests.py
19 files changed, 278 insertions(+), 93 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/ROADMAP.rst b/ROADMAP.rst
index c134af4..d0b9b2e 100644
--- a/ROADMAP.rst
+++ b/ROADMAP.rst
@@ -29,6 +29,9 @@
* Update translations (i18n).
* Add new :class:`family.WikimediaSubdomainFamily`.
* Update documentation for :meth:`page.BasePage.exists`. (:phab:`T334341`)
+* Public *reason* and *comment* parameters used for page-action and upload
+ summaries were renamed to *summary*. The old parameter names remain
+ available as deprecated aliases. (:phab:`T62442`)
Deprecations
@@ -131,6 +134,9 @@
Pending removal in Pywikibot 14
-------------------------------
+* 11.8.0: Public *reason* and *comment* parameters used for page-action and
+ upload summaries were renamed to *summary*. The old parameter names will
+ be removed. (:phab:`T62442`)
* 11.7.0: :meth:`page.BasePage.getOldVersion` is now deprecated in favour of
:meth:`get_revision(oldid, content=True).text<page.BasePage.get_revision>`.
(:phab:`T433799`)
* 11.7.0: The *lang* parameter of the :func:`i18n.altlang` function is
positional-only. Passing
diff --git a/pywikibot/page/_basepage.py b/pywikibot/page/_basepage.py
index 9a891b6..cf2bec4 100644
--- a/pywikibot/page/_basepage.py
+++ b/pywikibot/page/_basepage.py
@@ -672,7 +672,7 @@
self.site.review_revision(
revid,
- summary=summary,
+ comment=summary,
flag=flag,
)
@@ -718,7 +718,7 @@
self.site.review_revision(
revid,
- summary=summary,
+ comment=summary,
unapprove=True,
)
@@ -2145,9 +2145,10 @@
"""
self.site.merge_history(self, dest, timestamp, reason)
+ @deprecated_args(reason='summary') # since 11.8.0
def move(self,
newtitle: str,
- reason: str | None = None,
+ summary: str | None = None,
movetalk: bool = True,
noredirect: bool = False,
movesubpages: bool = True) -> pywikibot.page.Page:
@@ -2155,18 +2156,20 @@
.. version-changed:: 7.2
The *movesubpages* parameter was added
+ .. version-changed:: 11.8
+ The *reason* parameter was renamed to *summary*.
:param newtitle: The new page title.
- :param reason: The edit summary for the move.
+ :param summary: The edit summary for the move.
:param movetalk: If true, move this page's talk page (if it exists)
:param noredirect: If move succeeds, delete the old page
(usually requires sysop privileges, depending on wiki settings)
:param movesubpages: Rename subpages, if applicable.
"""
- if reason is None:
+ if summary is None:
pywikibot.info(f'Moving {self} to [[{newtitle}]].')
- reason = pywikibot.input('Please enter a reason for the move:')
- return self.site.movepage(self, newtitle, reason,
+ summary = pywikibot.input('Please enter a reason for the move:')
+ return self.site.movepage(self, newtitle, summary=summary,
movetalk=movetalk,
noredirect=noredirect,
movesubpages=movesubpages)
@@ -2215,9 +2218,10 @@
"""
return self.site.rollbackpage(self, **kwargs)
+ @deprecated_args(reason='summary') # since 11.8.0
def delete(
self,
- reason: str | None = None,
+ summary: str | None = None,
prompt: bool = True,
mark: bool = False,
automatic_quit: bool = False,
@@ -2231,6 +2235,8 @@
.. version-changed:: 11.2
*deletetalk* option was implemented for MediaWiki < 1.38wmf24.
+ .. version-changed:: 11.8
+ The *reason* parameter was renamed to *summary*.
.. seealso::
- :meth:`undelete`
@@ -2239,7 +2245,7 @@
- :meth:`site.APISite.delete
<pywikibot.site._apisite.APISite.delete>`
- :param reason: The edit summary for the deletion, or rationale
+ :param summary: The edit summary for the deletion, or rationale
for deletion if requesting. If None, ask for it.
:param prompt: If true, prompt user for confirmation before deleting.
:param mark: If true, and user does not have sysop rights, place a
@@ -2255,9 +2261,10 @@
1 page was deleted
-1 page was marked for deletion
"""
- if reason is None:
+ if summary is None:
pywikibot.info(f'Deleting {self.title(as_link=True)}.')
- reason = pywikibot.input('Please enter a reason for the deletion:')
+ summary = pywikibot.input(
+ 'Please enter a reason for the deletion:')
# If user has 'delete' right, delete the page
if self.site.has_right('delete'):
@@ -2272,7 +2279,8 @@
answer = 'y'
self.site._noDeletePrompt = True
if answer == 'y':
- self.site.delete(self, reason, deletetalk=deletetalk)
+ self.site.delete(
+ self, summary=summary, deletetalk=deletetalk)
return 1
return 0
@@ -2289,7 +2297,7 @@
answer = 'y'
self.site._noMarkDeletePrompt = True
if answer == 'y':
- template = '{{delete|1=%s}}\n' % reason
+ template = '{{delete|1=%s}}\n' % summary
# We can't add templates in a wikidata item, so let's use its
# talk page
if isinstance(self, pywikibot.ItemPage):
@@ -2299,7 +2307,7 @@
else:
target = self
target.text = template + target.text
- target.save(summary=reason)
+ target.save(summary=summary)
return -1
return 0
@@ -2400,7 +2408,8 @@
f'Timestamp {timestamp} is not a deleted revision')
self._deletedRevs[timestamp]['marked'] = undelete
- def undelete(self, reason: str | None = None) -> None:
+ @deprecated_args(reason='summary') # since 11.8.0
+ def undelete(self, summary: str | None = None) -> None:
"""Undelete revisions based on the markers set by previous calls.
If no calls have been made since :meth:`loadDeletedRevisions`,
@@ -2430,23 +2439,28 @@
- :meth:`site.APISite.undelete
<pywikibot.site._apisite.APISite.undelete>`
- :param reason: Reason for the action.
+ .. version-changed:: 11.8
+ The *reason* parameter was renamed to *summary*.
+
+ :param summary: Summary for the action.
"""
if hasattr(self, '_deletedRevs'):
undelete_revs = [ts for ts, rev in self._deletedRevs.items()
if rev.get('marked')]
else:
undelete_revs = []
- if reason is None:
+ if summary is None:
warn('Not passing a reason for undelete() is deprecated.',
DeprecationWarning, stacklevel=2)
pywikibot.info(f'Undeleting {self.title(as_link=True)}.')
- reason = pywikibot.input(
+ summary = pywikibot.input(
'Please enter a reason for the undeletion:')
- self.site.undelete(self, reason, revisions=undelete_revs)
+ self.site.undelete(
+ self, summary=summary, revisions=undelete_revs)
+ @deprecated_args(reason='summary') # since 11.8.0
def protect(self,
- reason: str | None = None,
+ summary: str | None = None,
protections: dict[str, str | None] | None = None,
**kwargs) -> None:
"""Protect or unprotect a wiki page. Requires *protect* right.
@@ -2470,7 +2484,10 @@
<pywikibot.site._apisite.APISite.protect>`
- :meth:`applicable_protections`
- :param reason: Reason for the action, default is None and will
+ .. version-changed:: 11.8
+ The *reason* parameter was renamed to *summary*.
+
+ :param summary: Summary for the action, default is None and will
set an empty string.
:param protections: A dict mapping type of protection to
protection level of that type. Allowed protection types for
@@ -2482,9 +2499,9 @@
"""
protections = protections or {} # protections is converted to {}
- reason = reason or '' # None is converted to ''
+ summary = summary or '' # None is converted to ''
- self.site.protect(self, protections, reason, **kwargs)
+ self.site.protect(self, protections, summary=summary, **kwargs)
def change_category(self, old_cat, new_cat,
summary: str | None = None,
diff --git a/pywikibot/page/_filepage.py b/pywikibot/page/_filepage.py
index 6fa662a..aa5a5c2 100644
--- a/pywikibot/page/_filepage.py
+++ b/pywikibot/page/_filepage.py
@@ -292,12 +292,15 @@
:param source: Path or URL to the file to be uploaded.
- :keyword comment: Edit summary; if this is not provided, then
+ .. version-changed:: 11.8
+ The *comment* keyword was renamed to *summary*.
+
+ :keyword summary: Edit summary; if this is not provided, then
filepage.text will be used. An empty summary is not
permitted. This may also serve as the initial page text (see
below).
:keyword text: Initial page text; if this is not set, then
- filepage.text will be used, or comment.
+ filepage.text will be used, or summary.
:keyword watch: If true, add filepage to the bot user's
watchlist
:keyword ignore_warnings: It may be a static boolean, a callable
diff --git a/pywikibot/site/_apisite.py b/pywikibot/site/_apisite.py
index 012e909..79fdb19 100644
--- a/pywikibot/site/_apisite.py
+++ b/pywikibot/site/_apisite.py
@@ -73,6 +73,7 @@
cached,
deprecate_arg,
deprecated,
+ deprecated_args,
deprecated_signature,
issue_deprecation_warning,
merge_unique_dicts,
@@ -2648,10 +2649,11 @@
} # other errors shouldn't occur because of pre-submission checks
@need_right('delete')
+ @deprecated_args(reason='summary') # since 11.8.0
def delete(
self,
page: BasePage | int | str,
- reason: str,
+ summary: str,
*,
deletetalk: bool = False,
oldimage: str | None = None
@@ -2679,13 +2681,16 @@
.. version-changed:: 11.2
*deletetalk* option was implemented for MediaWiki < 1.38wmf24.
+ .. version-changed:: 11.8
+ The *reason* parameter was renamed to *summary*.
+
.. seealso::
- :api:`Delete`
- :meth:`undelete`
- :meth:`page.BasePage.delete`
:param page: Page to be deleted or its pageid.
- :param reason: Deletion reason.
+ :param summary: Deletion summary.
:param deletetalk: Also delete the talk page, if it exists.
:param oldimage: Oldimage id of the file version to be deleted.
If a BasePage object is given with page parameter, it has to
@@ -2708,7 +2713,7 @@
params = {
'action': 'delete',
'token': token,
- 'reason': reason,
+ 'reason': summary,
'oldimage': oldimage,
}
@@ -2766,13 +2771,14 @@
'Cannot delete a non-existing associated talk page.'
)
else:
- self.delete(talk_page, reason)
+ self.delete(talk_page, summary=summary)
@need_right('undelete')
+ @deprecated_args(reason='summary') # since 11.8.0
def undelete(
self,
page: BasePage,
- reason: str,
+ summary: str,
*,
revisions: list[str] | None = None,
fileids: list[int | str] | None = None
@@ -2786,13 +2792,16 @@
`fileids` parameter was added,
keyword argument required for `revisions`.
+ .. version-changed:: 11.8
+ The *reason* parameter was renamed to *summary*.
+
.. seealso::
- :api:`Undelete`
- :meth:`delete`
- :meth:`page.BasePage.undelete`
:param page: Page to be deleted.
- :param reason: Undeletion reason.
+ :param summary: Undeletion summary.
:param revisions: List of timestamps to restore.
If None, restores all revisions.
:param fileids: List of fileids to restore.
@@ -2801,7 +2810,7 @@
params = {
'action': 'undelete',
'title': page,
- 'reason': reason,
+ 'reason': summary,
'token': token,
'timestamps': revisions,
'fileids': fileids,
@@ -2894,11 +2903,12 @@
}
@need_right('protect')
+ @deprecated_args(reason='summary') # since 11.8.0
def protect(
self,
page: BasePage,
protections: dict[str, str | None],
- reason: str,
+ summary: str,
expiry: datetime.datetime | str | None = None,
**kwargs: Any
) -> None:
@@ -2910,11 +2920,14 @@
- :meth:`page_restrictions`
- :api:`Protect`
+ .. version-changed:: 11.8
+ The *reason* parameter was renamed to *summary*.
+
:param protections: A dict mapping type of protection to
protection level of that type. Refer :meth:`restrictions`
for valid restriction types restriction levels. If None is
given, however, that protection will be skipped.
- :param reason: Reason for the action
+ :param summary: Summary for the action.
:param expiry: When the block should expire. This expiry will be
applied to all protections. If ``None``, ``'infinite'``,
``'indefinite'``, ``'never'``, or ``''`` is given, there is
@@ -2932,7 +2945,7 @@
title=page,
token=token,
protections=protections_list,
- reason=reason,
+ reason=summary,
expiry=expiry or None, # pass None instead of empty str
)
diff --git a/pywikibot/site/_upload.py b/pywikibot/site/_upload.py
index 030f5d0..dfc89f0 100644
--- a/pywikibot/site/_upload.py
+++ b/pywikibot/site/_upload.py
@@ -13,7 +13,7 @@
import pywikibot
from pywikibot.exceptions import APIError, Error, UploadError
-from pywikibot.tools import compute_file_hash
+from pywikibot.tools import compute_file_hash, deprecated_args
__all__ = ('Uploader', )
@@ -30,11 +30,11 @@
file will be obtained.
:param source_filename: path to the file to be uploaded
:param source_url: URL of the file to be uploaded
- :param comment: Edit summary; if this is not provided, then
+ :param summary: Edit summary; if this is not provided, then
filepage.text will be used. An empty summary is not permitted.
This may also serve as the initial page text (see below).
:param text: Initial page text; if this is not set, then
- filepage.text will be used, or comment.
+ filepage.text will be used, or summary.
:param watch: If true, add filepage to the bot user's watchlist
:param chunk_size: The chunk size in bytes for chunked uploading
(see :api:`Upload#Chunked_uploading`). It will only upload in
@@ -84,23 +84,28 @@
'version(s) of this file.',
}
+ @deprecated_args(comment='summary') # since 11.8.0
def __init__(self,
site: pywikibot.site.APISite,
filepage: pywikibot.FilePage,
*,
source_filename: str | None = None,
source_url: str | None = None,
- comment: str | None = None,
+ summary: str | None = None,
text: str | None = None,
watch: bool = False,
chunk_size: int = 0,
asynchronous: bool = False,
ignore_warnings=False,
report_success: bool | None = None) -> None:
- """Initializer."""
+ """Initializer.
+
+ .. version-changed:: 11.8
+ The *comment* parameter was renamed to *summary*.
+ """
self.site = site
self.filepage = filepage
- self.comment = comment
+ self.comment = summary
self.text = text
self.watch = watch
self.ignore_warnings = ignore_warnings
diff --git a/pywikibot/specialbots/_upload.py b/pywikibot/specialbots/_upload.py
index e4452fc..1bc679f 100644
--- a/pywikibot/specialbots/_upload.py
+++ b/pywikibot/specialbots/_upload.py
@@ -463,7 +463,7 @@
ignore_warnings=ignore_warnings,
chunk_size=self.chunk_size,
asynchronous=self.asynchronous,
- comment=self.summary)
+ summary=self.summary)
except APIError as error:
if error.code == 'uploaddisabled':
pywikibot.error(
diff --git a/scripts/category.py b/scripts/category.py
index f91148f..a5cc4e0 100755
--- a/scripts/category.py
+++ b/scripts/category.py
@@ -759,7 +759,7 @@
old_cat_title = self.oldcat.title()
old_cat_text = self.oldcat.text
self.newcat = self.oldcat.move(self.newcat.title(),
- reason=self.move_comment,
+ summary=self.move_comment,
movetalk=can_move_talk,
noredirect=self.noredirect)
# Copy over the article text so it can be stripped of
@@ -819,12 +819,12 @@
:type moved_talk: pywikibot.page.BasePage
"""
if moved_page and self.oldcat.exists():
- self.oldcat.delete(self.deletion_comment, not self.batch,
- mark=True)
+ self.oldcat.delete(summary=self.deletion_comment,
+ prompt=not self.batch, mark=True)
self.counter['delete'] += 1
if moved_talk and self.oldtalk.exists():
- self.oldtalk.delete(self.deletion_comment, not self.batch,
- mark=True)
+ self.oldtalk.delete(summary=self.deletion_comment,
+ prompt=not self.batch, mark=True)
self.counter['delete talk'] += 1
def _change(self, gen) -> None:
@@ -936,7 +936,7 @@
comment = i18n.twtranslate(self.site, 'category-was-moved',
{'newcat': cat_name_only,
'title': cat_name_only})
- self.oldtalk.move(self.newtalk.title(), comment)
+ self.oldtalk.move(self.newtalk.title(), summary=comment)
def _update_wikibase_item(self) -> None:
"""Private function to update the Wikibase item for the category.
diff --git a/scripts/delete.py b/scripts/delete.py
index e6fd86a..a34055b 100755
--- a/scripts/delete.py
+++ b/scripts/delete.py
@@ -183,7 +183,7 @@
def treat_page(self) -> None:
"""Process one page from the generator."""
if self.opt.undelete:
- self.current_page.undelete(self.summary)
+ self.current_page.undelete(summary=self.summary)
self.counter['undelete'] += 1
else:
if (self.opt.isorphan is not False
@@ -203,9 +203,9 @@
if self.current_page.site.user() is None:
self.current_page.site.login()
- res = self.current_page.delete(self.summary,
- not self.opt.always,
- self.opt.always,
+ res = self.current_page.delete(summary=self.summary,
+ prompt=not self.opt.always,
+ mark=self.opt.always,
automatic_quit=True)
if res > 0:
self.counter['delete'] += 1
diff --git a/scripts/imagetransfer.py b/scripts/imagetransfer.py
index 81feaa4..7933703 100755
--- a/scripts/imagetransfer.py
+++ b/scripts/imagetransfer.py
@@ -221,7 +221,7 @@
'imagetransfer-nowcommons_notice')
# try to delete the original image if we have a sysop account
if sourceSite.has_right('delete') \
- and sourceImagePage.delete(reason):
+ and sourceImagePage.delete(summary=reason):
return
tmpl = i18n.translate(sourceSite.code, nowCommonsTemplate)
diff --git a/scripts/movepages.py b/scripts/movepages.py
index 7049b39..2676324 100755
--- a/scripts/movepages.py
+++ b/scripts/movepages.py
@@ -86,7 +86,7 @@
msg = i18n.twtranslate(page.site, 'movepages-moving')
pywikibot.info(f'Moving page {page} to [[{new_page_tite}]]')
try:
- page.move(new_page_tite, reason=msg,
+ page.move(new_page_tite, summary=msg,
movetalk=self.opt.movetalkpage,
movesubpages=self.opt.movesubpages,
noredirect=self.opt.noredirect)
diff --git a/scripts/nowcommons.py b/scripts/nowcommons.py
index 13bbf6c..c58a1d6 100755
--- a/scripts/nowcommons.py
+++ b/scripts/nowcommons.py
@@ -374,7 +374,8 @@
'Does the description on Commons contain all required '
'source and license\ninformation?', default=False):
local_file_page.delete(
- f'{self.summary} [[:commons:File:{file_on_commons}]]',
+ summary=f'{self.summary} '
+ f'[[:commons:File:{file_on_commons}]]',
prompt=False)
self.counter['delete'] += 1
else:
diff --git a/scripts/protect.py b/scripts/protect.py
index 3a59c02..c1c6414 100755
--- a/scripts/protect.py
+++ b/scripts/protect.py
@@ -112,7 +112,7 @@
applicable = self.current_page.applicable_protections()
protections = dict(
prot for prot in self.protections.items() if prot[0] in applicable)
- self.current_page.protect(reason=self.opt.summary,
+ self.current_page.protect(summary=self.opt.summary,
expiry=self.opt.expiry,
protections=protections)
diff --git a/scripts/redirect.py b/scripts/redirect.py
index 7f68287..cb19913 100755
--- a/scripts/redirect.py
+++ b/scripts/redirect.py
@@ -463,7 +463,7 @@
f'target page is on different site {page.site}')
reason = i18n.twtranslate(page.site, summary_key, bot_prefix=True)
if page.site.has_right('delete'):
- page.delete(reason, prompt=False)
+ page.delete(summary=reason, prompt=False)
elif self.sdtemplate:
pywikibot.info('User does not have delete right, '
'put page to speedy deletion.')
diff --git a/scripts/speedy_delete.py b/scripts/speedy_delete.py
index c617b7b..1a88bdd 100755
--- a/scripts/speedy_delete.py
+++ b/scripts/speedy_delete.py
@@ -446,7 +446,7 @@
pywikibot.info(f'The chosen reason is: <<lightred>>{reason}')
# don't produce orphaned talk pages
deletetalk = not page.isTalkPage() and page.namespace() != 2
- page.delete(reason, prompt=False, deletetalk=deletetalk)
+ page.delete(summary=reason, prompt=False, deletetalk=deletetalk)
# skip this page
else:
diff --git a/tests/deletionbot_tests.py b/tests/deletionbot_tests.py
index 4aa8f2d..fccf228 100755
--- a/tests/deletionbot_tests.py
+++ b/tests/deletionbot_tests.py
@@ -147,17 +147,17 @@
self.assertEqual(self.undelete_args, ['[[FoooOoOooO]]', 'foo'])
-def delete_dummy(page_self, reason, prompt, mark, automatic_quit, *,
+def delete_dummy(page_self, summary, prompt, mark, automatic_quit, *,
deletetalk=False) -> int:
"""Dummy delete method."""
- TestDeletionBot.delete_args = [page_self.title(as_link=True), reason,
+ TestDeletionBot.delete_args = [page_self.title(as_link=True), summary,
prompt, mark, automatic_quit]
return 0
-def undelete_dummy(page_self, reason) -> None:
+def undelete_dummy(page_self, summary) -> None:
"""Dummy undelete method."""
- TestDeletionBot.undelete_args = [page_self.title(as_link=True), reason]
+ TestDeletionBot.undelete_args = [page_self.title(as_link=True), summary]
if __name__ == '__main__':
diff --git a/tests/flaggedrevs_tests.py b/tests/flaggedrevs_tests.py
index bc1e05e..3d62467 100644
--- a/tests/flaggedrevs_tests.py
+++ b/tests/flaggedrevs_tests.py
@@ -371,7 +371,7 @@
review_revision.assert_called_once_with(
revid,
- summary=None,
+ comment=None,
flag=None,
)
self.assertNotHasAttr(self.unflagged, '_stable_revision_id')
@@ -389,7 +389,7 @@
review_revision.assert_called_once_with(
revid,
- summary=None,
+ comment=None,
unapprove=True,
)
self.assertNotHasAttr(self.flagged, '_stable_revision_id')
diff --git a/tests/page_tests.py b/tests/page_tests.py
index c10bc1b..05c7cd5 100755
--- a/tests/page_tests.py
+++ b/tests/page_tests.py
@@ -7,6 +7,7 @@
"""Tests for the page module."""
from __future__ import annotations
+import inspect
import pickle
import re
import time
@@ -31,7 +32,12 @@
)
from pywikibot.tools import suppress_warnings
from tests import WARN_SITE_CODE, unittest_print
-from tests.aspects import DefaultSiteTestCase, SiteAttributeTestCase, TestCase
+from tests.aspects import (
+ DefaultSiteTestCase,
+ DeprecationTestCase,
+ SiteAttributeTestCase,
+ TestCase,
+)
from tests.utils import skipping
@@ -1169,6 +1175,47 @@
self.assertEqual(text, p2.get(get_redirect=True))
+class TestActionSummaryParameters(DeprecationTestCase):
+
+ """Test action summary parameters in Page methods."""
+
+ net = False
+
+ def test_signatures(self) -> None:
+ """Public signatures expose the new name and deprecated alias."""
+ methods = (
+ pywikibot.page.BasePage.move,
+ pywikibot.page.BasePage.delete,
+ pywikibot.page.BasePage.undelete,
+ pywikibot.page.BasePage.protect,
+ )
+ for method in methods:
+ with self.subTest(method=method.__name__):
+ parameters = inspect.signature(method).parameters
+ self.assertIn('summary', parameters)
+ self.assertIn('reason', parameters)
+
+ def test_reason_alias(self) -> None:
+ """The reason alias forwards its value to the summary position."""
+ page = mock.MagicMock()
+ result = pywikibot.page.BasePage.move(
+ page, 'New title', reason='Legacy summary')
+
+ self.assertEqual(result, page.site.movepage.return_value)
+ page.site.movepage.assert_called_once_with(
+ page,
+ 'New title',
+ summary='Legacy summary',
+ movetalk=True,
+ noredirect=False,
+ movesubpages=True,
+ )
+ self.assertOneDeprecationParts(
+ 'reason argument of pywikibot.page._basepage.BasePage.move',
+ 'summary',
+ )
+
+
class TestPageUserAction(DefaultSiteTestCase):
"""Test page user actions."""
@@ -1236,7 +1283,7 @@
p.save('Pywikibot unit test')
# Test deletion
- res = p.delete(reason='Pywikibot unit test', prompt=False, mark=False)
+ res = p.delete(summary='Pywikibot unit test', prompt=False, mark=False)
self.assertEqual(p.pageid, 0)
self.assertEqual(res, 1)
with self.assertRaisesRegex(NoPageError, NO_PAGE_RE):
@@ -1249,7 +1296,7 @@
p.markDeletedRevision(del_revs[-2])
with self.assertRaisesRegex(ValueError, 'is not a deleted revision'):
p.markDeletedRevision(123)
- p.undelete(reason='Pywikibot unit test')
+ p.undelete(summary='Pywikibot unit test')
revs = list(p.revisions())
self.assertLength(revs, 2)
self.assertEqual(revs[1].revid, revid)
@@ -1296,13 +1343,13 @@
p1 = pywikibot.Page(site, 'User:Unicodesnowman/ProtectTest')
p1.protect(protections={'edit': 'sysop', 'move': 'autoconfirmed'},
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(p1.protection(),
{'edit': ('sysop', 'infinite'),
'move': ('autoconfirmed', 'infinite')})
p1.protect(protections={'edit': '', 'move': ''},
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(p1.protection(), {})
def test_protect_with_empty_parameters(self) -> None:
@@ -1311,12 +1358,12 @@
p1 = pywikibot.Page(site, 'User:Unicodesnowman/ProtectTest')
p1.protect(protections={'edit': 'sysop', 'move': 'autoconfirmed'},
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(p1.protection(),
{'edit': ('sysop', 'infinite'),
'move': ('autoconfirmed', 'infinite')})
- p1.protect(reason='Pywikibot unit test')
+ p1.protect(summary='Pywikibot unit test')
self.assertEqual(p1.protection(), {})
def test_protect_alt(self) -> None:
@@ -1325,14 +1372,14 @@
p1 = pywikibot.Page(site, 'User:Unicodesnowman/ProtectTest')
p1.protect(protections={'edit': 'sysop', 'move': 'autoconfirmed'},
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(p1.protection(),
{'edit': ('sysop', 'infinite'),
'move': ('autoconfirmed', 'infinite')})
# workaround
p1 = pywikibot.Page(site, 'User:Unicodesnowman/ProtectTest')
p1.protect(protections={'edit': '', 'move': ''},
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(p1.protection(), {})
diff --git a/tests/site_tests.py b/tests/site_tests.py
index d199898..9f0dfa1 100755
--- a/tests/site_tests.py
+++ b/tests/site_tests.py
@@ -7,12 +7,14 @@
"""Tests for the site module."""
from __future__ import annotations
+import inspect
import pickle
import random
import threading
import unittest
from collections.abc import Iterable, Mapping
from contextlib import suppress
+from unittest import mock
from unittest.mock import patch
import pywikibot
@@ -552,6 +554,70 @@
next(gen)
+class TestActionSummaryParameters(DeprecationTestCase):
+
+ """Test action summary parameters in APISite methods."""
+
+ net = False
+
+ methods = (
+ pywikibot.site.APISite.delete,
+ pywikibot.site.APISite.undelete,
+ pywikibot.site.APISite.protect,
+ )
+
+ def setUp(self) -> None:
+ """Create a mock site which can pass the rights decorator."""
+ super().setUp()
+ self.mock_site = mock.MagicMock()
+ self.mock_site.obsolete = False
+ self.mock_site.has_right.return_value = True
+ self.mock_site.tokens = {'csrf': 'TOKEN'}
+ self.mock_site.simple_request.return_value.submit.return_value = {}
+ self.page = mock.MagicMock()
+
+ def test_signatures(self) -> None:
+ """Public signatures expose the new name and deprecated alias."""
+ for method in self.methods:
+ with self.subTest(method=method.__name__):
+ parameters = inspect.signature(method).parameters
+ self.assertIn('summary', parameters)
+ self.assertIn('reason', parameters)
+
+ def test_summary_maps_to_reason(self) -> None:
+ """The public summary is sent using MediaWiki's reason key."""
+ pywikibot.site.APISite.undelete(
+ self.mock_site, self.page, summary='Canonical summary')
+
+ self.mock_site.simple_request.assert_called_once_with(
+ action='undelete',
+ title=self.page,
+ reason='Canonical summary',
+ token='TOKEN',
+ timestamps=None,
+ fileids=None,
+ )
+ self.assertNoDeprecation()
+
+ def test_reason_alias(self) -> None:
+ """The deprecated reason alias maps to the new public parameter."""
+ pywikibot.site.APISite.undelete(
+ self.mock_site, self.page, reason='Legacy summary')
+
+ self.mock_site.simple_request.assert_called_once_with(
+ action='undelete',
+ title=self.page,
+ reason='Legacy summary',
+ token='TOKEN',
+ timestamps=None,
+ fileids=None,
+ )
+ self.assertOneDeprecationParts(
+ 'reason argument of pywikibot.site._apisite.APISite.undelete',
+ 'summary',
+ )
+
+
class TestSiteSysopWrite(TestCase):
"""Test site methods that require writing rights."""
@@ -570,7 +636,7 @@
r = site.protect(protections={'edit': 'sysop',
'move': 'autoconfirmed'},
page=p1,
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertIsNone(r)
self.assertEqual(site.page_restrictions(page=p1),
{'edit': ('sysop', 'infinite'),
@@ -580,7 +646,7 @@
site.protect(protections={'edit': 'sysop', 'move': 'autoconfirmed'},
page=p1,
expiry=expiry,
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(site.page_restrictions(page=p1),
{'edit': ('sysop', '2050-01-01T00:00:00Z'),
@@ -588,7 +654,7 @@
site.protect(protections={'edit': '', 'move': ''},
page=p1,
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(site.page_restrictions(page=p1), {})
def test_protect_alt(self) -> None:
@@ -599,7 +665,7 @@
r = site.protect(protections={'edit': 'sysop',
'move': 'autoconfirmed'},
page=p1,
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertIsNone(r)
self.assertEqual(site.page_restrictions(page=p1),
{'edit': ('sysop', 'infinite'),
@@ -610,7 +676,7 @@
site.protect(protections={'edit': 'sysop', 'move': 'autoconfirmed'},
page=p1,
expiry=expiry,
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(site.page_restrictions(page=p1),
{'edit': ('sysop', '2050-01-01T00:00:00Z'),
@@ -619,7 +685,7 @@
p1 = pywikibot.Page(site, 'User:Unicodesnowman/ProtectTest')
site.protect(protections={'edit': '', 'move': ''},
page=p1,
- reason='Pywikibot unit test')
+ summary='Pywikibot unit test')
self.assertEqual(site.page_restrictions(page=p1), {})
def test_protect_exception(self) -> None:
@@ -631,12 +697,12 @@
self.assertRaisesRegex(APIError,
'Invalid protection type "anInvalidType"'):
site.protect(protections={'anInvalidType': 'sysop'},
- page=page, reason='Pywikibot unit test')
+ page=page, summary='Pywikibot unit test')
with self.subTest(test='anInvalidLevel'), \
self.assertRaisesRegex(Error, 'Invalid protection level'):
site.protect(protections={'edit': 'anInvalidLevel'},
- page=page, reason='Pywikibot unit test')
+ page=page, summary='Pywikibot unit test')
def test_delete(self) -> None:
"""Test the site.delete() and site.undelete() methods."""
@@ -646,7 +712,7 @@
if not p.exists():
site.undelete(p, 'pywikibot unit tests')
- site.delete(p, reason='pywikibot unit tests')
+ site.delete(p, summary='pywikibot unit tests')
with self.assertRaises(NoPageError):
p.get(force=True)
@@ -659,7 +725,7 @@
self.assertEqual(revs[0].revid, 219995)
self.assertEqual(revs[1].revid, 219994)
- site.delete(p, reason='pywikibot unit tests')
+ site.delete(p, summary='pywikibot unit tests')
site.undelete(p, 'pywikibot unit tests')
revs = list(p.revisions())
self.assertGreater(len(revs), 2)
diff --git a/tests/upload_tests.py b/tests/upload_tests.py
index c45f82f..a4397b3 100755
--- a/tests/upload_tests.py
+++ b/tests/upload_tests.py
@@ -9,12 +9,13 @@
import unittest
from contextlib import suppress
+from unittest import mock
import pywikibot
from pywikibot.site._upload import Uploader
from pywikibot.tools import compute_file_hash
from tests import join_images_path
-from tests.aspects import TestCase
+from tests.aspects import DeprecationTestCase, TestCase
from tests.utils import DryRequest, DrySite
@@ -121,7 +122,7 @@
page = _FilePage()
uploader = Uploader(
site, page, source_filename=self.source,
- comment='upload test', chunk_size=1024,
+ summary='upload test', chunk_size=1024,
ignore_warnings=callback)
return uploader, site, page
@@ -223,7 +224,7 @@
site = _Site([], stash_info)
uploader = Uploader(
site, _FilePage(), source_filename=source,
- comment='upload test', chunk_size=1024,
+ summary='upload test', chunk_size=1024,
ignore_warnings=True)
with self.assertRaises(ValueError) as cm:
@@ -272,7 +273,7 @@
page = _FilePage()
uploader = Uploader(
site, page, source_url='https://example.invalid/Test.png',
- comment='upload test', ignore_warnings=lambda warnings: True)
+ summary='upload test', ignore_warnings=lambda warnings: True)
self.assertTrue(uploader.upload())
submitted = [request for request in site.requests
@@ -289,7 +290,7 @@
page = _FilePage()
uploader = Uploader(
site, page, source_url='https://example.invalid/Test.png',
- comment='upload test')
+ summary='upload test')
request = site.simple_request(action='upload', token='token')
self.assertTrue(uploader.submit(
@@ -297,6 +298,32 @@
self.assertIsNotNone(page.revisions)
+class TestUploadSummaryParameter(DeprecationTestCase):
+
+ """Test the upload summary parameter and its compatibility alias."""
+
+ net = False
+
+ def test_summary(self) -> None:
+ """Uploader stores the canonical summary without a warning."""
+ uploader = Uploader(
+ mock.MagicMock(), mock.MagicMock(), summary='Canonical summary')
+
+ self.assertEqual(uploader.comment, 'Canonical summary')
+ self.assertNoDeprecation()
+
+ def test_comment_alias(self) -> None:
+ """The deprecated comment alias maps to the upload summary."""
+ uploader = Uploader(
+ mock.MagicMock(), mock.MagicMock(), comment='Legacy summary')
+
+ self.assertEqual(uploader.comment, 'Legacy summary')
+ self.assertOneDeprecationParts(
+ 'comment argument of pywikibot.site._upload.Uploader.__init__',
+ 'summary',
+ )
+
+
class TestUpload(TestCase):
"""Test cases for upload."""
@@ -314,7 +341,7 @@
"""Test uploading a png using Site.upload."""
page = pywikibot.FilePage(self.site, 'MP_sounds-pwb.png')
self.site.upload(page, source_filename=self.sounds_png,
- comment='pywikibot test',
+ summary='pywikibot test',
ignore_warnings=True)
def test_png_chunked(self) -> None:
@@ -337,7 +364,7 @@
self.assertNotEqual(current_sha1, source_sha1)
self.assertTrue(
self.site.upload(page, source_filename=source_filename,
- comment='pywikibot test',
+ summary='pywikibot test',
ignore_warnings=True, chunk_size=1024))
def _init_upload(self, chunk_size) -> None:
@@ -365,7 +392,7 @@
self.assertNotHasAttr(self, '_file_key')
self.assertFalse(
self.site.upload(page, source_filename=self.sounds_png,
- comment='pywikibot test', chunk_size=chunk_size,
+ summary='pywikibot test', chunk_size=chunk_size,
ignore_warnings=warn_callback))
# Check that the warning happened and it's cached
@@ -388,7 +415,7 @@
# Finish/continue upload with the given file key
page = pywikibot.FilePage(self.site, 'MP_sounds-pwb.png')
self.site.upload(page, source_filename=file_name,
- comment='pywikibot test', chunk_size=chunk_size,
+ summary='pywikibot test', chunk_size=chunk_size,
ignore_warnings=True, report_success=False)
def _test_continue_filekey(self, chunk_size) -> None:
@@ -397,7 +424,7 @@
page = pywikibot.FilePage(self.site, 'MP_sounds-pwb.png')
uploader = Uploader(
self.site, page, source_filename=self.sounds_png,
- comment='pywikibot test', text=page.text,
+ summary='pywikibot test', text=page.text,
chunk_size=chunk_size,
ignore_warnings=True, report_success=False)
self.assertTrue(uploader._upload(
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1325915?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.wikimedia.org/r/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: I3384d6d97121abc9f7729c0ada7c22ccf4c98e59
Gerrit-Change-Number: 1325915
Gerrit-PatchSet: 6
Gerrit-Owner: Mahveotm <[email protected]>
Gerrit-Reviewer: Tacsipacsi <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]