jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1328725?usp=email )
Change subject: page: Do not validate sections in BasePage.get
......................................................................
page: Do not validate sections in BasePage.get
BasePage.get retrieves the complete page text regardless of a title
fragment. Move the existing best-effort raw-wikitext check to the
opt-in strict redirect path so ignore_section=False remains functional.
Bug: T422856
Bug: T422859
Change-Id: Ia86ddae21bb52e4bc8948a7bb984ebb5b15d663c
---
M ROADMAP.rst
M pywikibot/page/_basepage.py
M pywikibot/page/_page.py
M pywikibot/page/_wikibase.py
M pywikibot/site/_apisite.py
M tests/page_tests.py
6 files changed, 134 insertions(+), 32 deletions(-)
Approvals:
JJMC89: Looks good to me, approved
jenkins-bot: Verified
diff --git a/ROADMAP.rst b/ROADMAP.rst
index 941279c..3e7ad59 100644
--- a/ROADMAP.rst
+++ b/ROADMAP.rst
@@ -4,6 +4,9 @@
* Update translations (i18n)
* Add new :class:`family.WikimediaSubdomainFamily`
* Update documentation for :meth:`page.BasePage.exists`. (:phab:`T334341`)
+* :meth:`page.BasePage.get` no longer validates section fragments or raises
+ :exc:`exceptions.SectionError` for them (:phab:`T422856`,
+ :phab:`T422859`).
Deprecations
============
diff --git a/pywikibot/page/_basepage.py b/pywikibot/page/_basepage.py
index d7b4e4a..020c5d1 100644
--- a/pywikibot/page/_basepage.py
+++ b/pywikibot/page/_basepage.py
@@ -379,8 +379,10 @@
pywikibot.exceptions.IsRedirectPageError: ... is a redirect page.
.. version-changed:: 9.2
- :exc:`exceptions.SectionError` is raised if the
- :meth:`section` does not exist
+ Added validation for the section in the page title.
+ .. version-changed:: 11.8
+ A title fragment neither selects nor validates a section; the
+ complete page text is returned.
.. seealso:: :attr:`text` property
:param force: Reload all page attributes, including errors.
@@ -388,8 +390,6 @@
redirect, do not raise an exception.
:raises NoPageError: The page does not exist.
:raises IsRedirectPageError: The page is a redirect.
- :raises SectionError: The section does not exist on a page with
- a # link.
"""
if force:
del self.latest_revision_id
@@ -401,17 +401,7 @@
if not get_redirect:
raise
- text = self.latest_revision.text
-
- # check for valid section in title
- page_section = self.section()
- if page_section:
- content = textlib.extract_sections(text, self.site)
- if page_section not in content.sections:
- raise SectionError(f'{page_section!r} is not a valid section '
- f'of {self.title(with_section=False)}')
-
- return text
+ return self.latest_revision.text
def has_content(self) -> bool:
"""Page has been loaded.
@@ -2026,16 +2016,16 @@
<pywikibot.site._apisite.APISite.getredirtarget>`
* :meth:`moved_target`
- :param ignore_section: Do not include section to the target even
- the link has one
+ :param ignore_section: Skip checking the target section against raw
+ wikitext headings.
:raises CircularRedirectError: Page is a circular redirect
:raises InterwikiRedirectPageError: The redirect target is on
another site
:raises IsNotRedirectPageError: Page is not a redirect
:raises RuntimeError: No redirects found
- :raises SectionError: The section is not found on target page
- and *ignore_section* is not set
+ :raises SectionError: The section does not match a raw wikitext
+ heading on the target page and *ignore_section* is not set
"""
return self.site.getredirtarget(self, ignore_section=ignore_section)
diff --git a/pywikibot/page/_page.py b/pywikibot/page/_page.py
index ea213bc..6c1de70 100644
--- a/pywikibot/page/_page.py
+++ b/pywikibot/page/_page.py
@@ -23,6 +23,7 @@
IsNotRedirectPageError,
IsRedirectPageError,
NoPageError,
+ SectionError,
UnknownExtensionError,
)
from pywikibot.page._basepage import BasePage
@@ -45,6 +46,28 @@
'if source is a Site.')
super().__init__(source, title, ns)
+ def _check_section(self) -> None:
+ """Check the title section against headings in the raw wikitext.
+
+ This best-effort check supports strict redirect-target handling; it
+ does not reproduce MediaWiki's parser-generated anchors.
+
+ :raises SectionError: The section does not match an extracted heading.
+ """
+ page_section = self.section()
+ if not page_section:
+ return
+
+ try:
+ text = self.get(get_redirect=True)
+ except NoPageError:
+ return
+
+ content = textlib.extract_sections(text, self.site)
+ if page_section not in content.sections:
+ raise SectionError(f'{page_section!r} is not a valid section '
+ f'of {self.title(with_section=False)}')
+
@property
@cached
def raw_extracted_templates(self):
diff --git a/pywikibot/page/_wikibase.py b/pywikibot/page/_wikibase.py
index 87a0217..4403d57 100644
--- a/pywikibot/page/_wikibase.py
+++ b/pywikibot/page/_wikibase.py
@@ -1165,8 +1165,8 @@
.. seealso:: :meth:`page.BasePage.getRedirectTarget`
- :param ignore_section: Do not include section to the target even
- the link has one
+ :param ignore_section: Skip checking the target section against raw
+ wikitext headings.
:raises CircularRedirectError: Page is a circular redirect
:raises InterwikiRedirectPageError: The redirect target is on
@@ -1174,8 +1174,8 @@
:raises Error: Target page has wrong content model
:raises IsNotRedirectPageError: Page is not a redirect
:raises RuntimeError: No redirects found
- :raises SectionError: The section is not found on target page
- and *ignore_section* is not set
+ :raises SectionError: The section does not match a raw wikitext
+ heading on the target page and *ignore_section* is not set
"""
target = super().getRedirectTarget(ignore_section=ignore_section)
cmodel = target.content_model
diff --git a/pywikibot/site/_apisite.py b/pywikibot/site/_apisite.py
index c2d2303..012e909 100644
--- a/pywikibot/site/_apisite.py
+++ b/pywikibot/site/_apisite.py
@@ -1633,8 +1633,8 @@
.. seealso:: :meth:`page.BasePage.getRedirectTarget`
:param page: Page to search redirects for
- :param ignore_section: Do not include section to the target even
- the link has one
+ :param ignore_section: Skip checking the target section against raw
+ wikitext headings.
:return: Redirect target of page
:raises CircularRedirectError: Page is a circular redirect
@@ -1642,8 +1642,8 @@
another site
:raises IsNotRedirectPageError: Page is not a redirect
:raises RuntimeError: No redirects found
- :raises SectionError: The section is not found on target page
- and *ignore_section* is not set
+ :raises SectionError: The section does not match a raw wikitext
+ heading on the target page and *ignore_section* is not set
"""
if not self.page_isredirect(page):
raise IsNotRedirectPageError(page)
@@ -1726,8 +1726,7 @@
target = pywikibot.Category(target)
if not ignore_section:
- # get the content; this raises SectionError if section is not found
- target.text
+ target._check_section()
page._redirtarget = target
return page._redirtarget
diff --git a/tests/page_tests.py b/tests/page_tests.py
index 3f158cd..b12547f 100755
--- a/tests/page_tests.py
+++ b/tests/page_tests.py
@@ -186,6 +186,95 @@
section.assert_called_once_with()
+class TestPageGet(DefaultSiteTestCase):
+
+ """Test retrieving page text."""
+
+ dry = True
+
+ def _page_with_text(self, title: str, text: str) -> pywikibot.Page:
+ """Return a page with cached *text*."""
+ page = pywikibot.Page(self.site, title)
+ page._revid = 1
+ page._isredir = False
+ page._revisions[1] = pywikibot.page.Revision(
+ revid=1, slots={'main': {'*': text}})
+ return page
+
+ def test_get_does_not_validate_section(self) -> None:
+ """Test that get does not inspect a title section."""
+ text = '== Existing ==\nText'
+ page = self._page_with_text('Test#Missing', text)
+
+ with mock.patch.object(page, 'section') as section:
+ self.assertEqual(page.get(), text)
+ section.assert_not_called()
+
+ def test_text_section_regressions(self) -> None:
+ """Test parser-normalized headings do not affect page text."""
+ cases = (
+ (
+ 'T422856',
+ 'Test#Radio stations in India by state',
+ '==== Radio stations in India by state ====\nContent',
+ ),
+ (
+ 'T411307',
+ 'Test#Japanese superhero films by decade',
+ '==== Japanese superhero films\u200e by decade ====\nContent',
+ ),
+ )
+
+ for task, title, text in cases:
+ with self.subTest(task=task):
+ page = self._page_with_text(title, text)
+ self.assertEqual(page.text, text)
+
+ def test_check_section(self) -> None:
+ """Test explicit section validation."""
+ page = self._page_with_text('Test#Existing', '== Existing ==\nText')
+ page._check_section()
+
+ page = self._page_with_text('Test#Missing', '== Existing ==\nText')
+
+ with self.assertRaisesRegex(SectionError,
+ "'Missing' is not a valid section"):
+ page._check_section()
+
+ def test_check_section_missing_page(self) -> None:
+ """Test strict redirect checking preserves a missing target."""
+ page = pywikibot.Page(self.site, 'Missing#Section')
+ page._getexception = NoPageError(page)
+
+ page._check_section()
+
+ def test_redirect_target_checks_section(self) -> None:
+ """Test explicit redirect section validation is preserved."""
+ page = pywikibot.Page(self.site, 'Redirect')
+ page._isredir = True
+ data = {
+ 'query': {
+ 'redirects': [{
+ 'from': 'Redirect',
+ 'to': 'Target',
+ 'tofragment': 'Section',
+ }],
+ 'pages': {'1': {'title': 'Target'}},
+ },
+ }
+ request = mock.Mock()
+ request.submit.return_value = data
+
+ with mock.patch.object(self.site, 'simple_request',
+ return_value=request):
+ with mock.patch.object(
+ pywikibot.Page, '_check_section') as check_section:
+ target = page.getRedirectTarget(ignore_section=False)
+
+ self.assertEqual(target.title(), 'Target#Section')
+ check_section.assert_called_once_with()
+
+
class TestPageObjectEnglish(TestCase):
"""Test Page Object using English Wikipedia."""
@@ -1052,9 +1141,7 @@
p3.get()
page = pywikibot.Page(site, 'User:Legoktm/R2#Section')
- with self.assertRaisesRegex(SectionError,
- "'Section' is not a valid section"):
- page.get()
+ self.assertEqual(page.get(), text)
site = pywikibot.Site('mediawiki')
page = pywikibot.Page(site, 'Manual:Pywikibot/2.0 #See_also')
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1328725?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: Ia86ddae21bb52e4bc8948a7bb984ebb5b15d663c
Gerrit-Change-Number: 1328725
Gerrit-PatchSet: 3
Gerrit-Owner: Mahveotm <[email protected]>
Gerrit-Reviewer: JJMC89 <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]