jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/589016 )
Change subject: [cleanup] instantiate a CosmeticChangesToolkit by passing a page
......................................................................
[cleanup] instantiate a CosmeticChangesToolkit by passing a page
The CosmeticChangesToolkit interface was expanded step by step
but the parameters were kept due to historical reason even though
most of them can be derived from page object directly.
- instantiate the CosmeticChangesToolkit from a page object
- only allow keyword arguments except for page parameter
- update doc of initializer
- show a FutureWarning if a CosmeticChangesToolkit is still created
by site, namespace and pageTitle
- raise a TypeError if a CosmeticChangesToolkit is created by a page
object and namespace or pageTitle are still given
- show FutureWarning to deprecate from_page method
- update CosmeticChangesToolkit calls in page library
and cosmetic_changes.py
A FutureWarning will be shown in favour of DeprecationWarning to
switch the code by passing a page very shortly. This gives us the
ability to get and set the text from and to the page object
directly if applicable.
Change-Id: Ic98fe7a01a221972c6474871679769a98240d99e
---
M pywikibot/cosmetic_changes.py
M pywikibot/page/__init__.py
M scripts/cosmetic_changes.py
3 files changed, 57 insertions(+), 24 deletions(-)
Approvals:
Hazard-SJ: Looks good to me, but someone else must approve
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/cosmetic_changes.py b/pywikibot/cosmetic_changes.py
index f84d6c8..dcf3af5 100755
--- a/pywikibot/cosmetic_changes.py
+++ b/pywikibot/cosmetic_changes.py
@@ -58,6 +58,8 @@
#
import re
+from typing import Optional
+
import pywikibot
from pywikibot.page import url2unicode
@@ -66,7 +68,8 @@
_get_regexes, _MultiTemplateMatchBuilder, FILE_LINK_REGEX
)
from pywikibot.tools import (
- deprecated, deprecated_args, first_lower, first_upper
+ deprecated, deprecated_args, first_lower, first_upper,
+ issue_deprecation_warning,
)
try:
@@ -203,20 +206,47 @@
"""Cosmetic changes toolkit."""
- @deprecated_args(debug='show_diff', redirect=None, diff='show_diff')
- def __init__(self, site, show_diff=False, namespace=None, pageTitle=None,
- ignore=CANCEL_ALL):
- """Initializer."""
- self.site = site
+ @deprecated_args(redirect=None, diff='show_diff', site='page')
+ def __init__(self, page, *,
+ show_diff: bool = False,
+ namespace: Optional[int] = None,
+ pageTitle: Optional[str] = None,
+ ignore: int = CANCEL_ALL):
+ """Initializer.
+
+ @param page: the Page object containing the text to be modified
+ @type page: pywikibot.Page
+ @param show_diff: show difference after replacements (default: False)
+ @param namespace: DEPRECATED namespace parameter
+ @param pageTitle: DEPRECATED page title parameter
+ @param ignore: ignores if an error occurred and either skips the page
+ or only that method. It can be set one of the CANCEL constants
+ """
+ if isinstance(page, pywikibot.BaseSite):
+ self.site = page
+ self.title = pageTitle
+ try:
+ self.namespace = self.site.namespaces.resolve(namespace).pop(0)
+ except (KeyError, TypeError, IndexError):
+ raise ValueError('{} needs a valid namespace'
+ .format(self.__class__.__name__))
+ issue_deprecation_warning(
+ 'site parameter of CosmeticChangesToolkit',
+ 'a pywikibot.Page object as first parameter',
+ warning_class=FutureWarning,
+ since='20201102')
+ else:
+ if namespace is not None or pageTitle is not None:
+ raise TypeError(
+ "'namespace' and 'pageTitle' arguments are invalid with "
+ 'a given Page object')
+ self.site = page.site
+ self.title = page.title()
+ self.namespace = page.namespace()
+
self.show_diff = show_diff
- try:
- self.namespace = self.site.namespaces.resolve(namespace).pop(0)
- except (KeyError, TypeError, IndexError):
- raise ValueError('{0} needs a valid namespace'
- .format(self.__class__.__name__))
self.template = (self.namespace == 10)
self.talkpage = self.namespace >= 0 and self.namespace % 2 == 1
- self.title = pageTitle
self.ignore = ignore
self.common_methods = [
@@ -258,11 +288,12 @@
self.show_diff = bool(value)
@classmethod
+ @deprecated('CosmeticChangesToolkit with pywikibot.Page object',
+ future_warning=True, since='20200415')
@deprecated_args(diff='show_diff')
def from_page(cls, page, show_diff=False, ignore=CANCEL_ALL):
"""Create toolkit based on the page."""
- return cls(page.site, show_diff=show_diff, namespace=page.namespace(),
- pageTitle=page.title(), ignore=ignore)
+ return cls(page, show_diff=show_diff, ignore=ignore)
def safe_execute(self, method, text):
"""Execute the method and catch exceptions if enabled."""
diff --git a/pywikibot/page/__init__.py b/pywikibot/page/__init__.py
index 9ba81df..bf1f657 100644
--- a/pywikibot/page/__init__.py
+++ b/pywikibot/page/__init__.py
@@ -1303,9 +1303,7 @@
# cc depends on page directly and via several other imports
from pywikibot.cosmetic_changes import (
CANCEL_MATCH, CosmeticChangesToolkit)
- cc_toolkit = CosmeticChangesToolkit(
- self.site, namespace=self.namespace(), pageTitle=self.title(),
- ignore=CANCEL_MATCH)
+ cc_toolkit = CosmeticChangesToolkit(self, ignore=CANCEL_MATCH)
self.text = cc_toolkit.change(old)
if summary and old.strip().replace(
'\r\n', '\n') != self.text.strip().replace('\r\n', '\n'):
diff --git a/scripts/cosmetic_changes.py b/scripts/cosmetic_changes.py
index 7933990..02abc43 100644
--- a/scripts/cosmetic_changes.py
+++ b/scripts/cosmetic_changes.py
@@ -34,8 +34,12 @@
#
import pywikibot
-from pywikibot import config, cosmetic_changes, i18n, pagegenerators
+from pywikibot import config, i18n, pagegenerators
from pywikibot.bot import MultipleSitesBot, ExistingPageBot, NoRedirectPageBot
+from pywikibot.cosmetic_changes import (
+ CANCEL_ALL, CANCEL_MATCH, CANCEL_METHOD, CANCEL_PAGE,
+ CosmeticChangesToolkit,
+)
from pywikibot.tools import PYTHON_VERSION
if PYTHON_VERSION >= (3, 9):
@@ -65,7 +69,7 @@
self.available_options.update({
'async': False,
'summary': 'Robot: Cosmetic changes',
- 'ignore': cosmetic_changes.CANCEL_ALL,
+ 'ignore': CANCEL_ALL,
})
super().__init__(**kwargs)
@@ -73,8 +77,8 @@
def treat_page(self) -> None:
"""Treat page with the cosmetic toolkit."""
- cc_toolkit = cosmetic_changes.CosmeticChangesToolkit.from_page(
- self.current_page, ignore=self.opt.ignore)
+ cc_toolkit = CosmeticChangesToolkit(self.current_page,
+ ignore=self.opt.ignore)
changed_text = cc_toolkit.change(self.current_page.get())
if changed_text is not False:
self.put_current(new_text=changed_text,
@@ -106,11 +110,11 @@
elif arg.startswith('-ignore:'):
ignore_mode = arg[len('-ignore:'):].lower()
if ignore_mode == 'method':
- options['ignore'] = cosmetic_changes.CANCEL_METHOD
+ options['ignore'] = CANCEL_METHOD
elif ignore_mode == 'page':
- options['ignore'] = cosmetic_changes.CANCEL_PAGE
+ options['ignore'] = CANCEL_PAGE
elif ignore_mode == 'match':
- options['ignore'] = cosmetic_changes.CANCEL_MATCH
+ options['ignore'] = CANCEL_MATCH
else:
raise ValueError(
'Unknown ignore mode "{0}"!'.format(ignore_mode))
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/589016
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: Ic98fe7a01a221972c6474871679769a98240d99e
Gerrit-Change-Number: 589016
Gerrit-PatchSet: 5
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: D3r1ck01 <xsavitar.wiki(a)aol.com>
Gerrit-Reviewer: Hazard-SJ <hazardsjwiki(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/+/638071 )
Change subject: [cleanup] Remove unreachable line in solve_disambiguation.py
......................................................................
[cleanup] Remove unreachable line in solve_disambiguation.py
Unreacheble line exists for more than 15 years;
It can be removed then.
Bug: T155337
Change-Id: Idf92aa58f817b8c8f65404242919ee99476aa52e
---
M scripts/solve_disambiguation.py
1 file changed, 76 insertions(+), 57 deletions(-)
Approvals:
Hazard-SJ: Looks good to me, approved
jenkins-bot: Verified
diff --git a/scripts/solve_disambiguation.py b/scripts/solve_disambiguation.py
index ff428db..4cdc94a 100755
--- a/scripts/solve_disambiguation.py
+++ b/scripts/solve_disambiguation.py
@@ -822,9 +822,10 @@
if n == 0:
# No changes necessary for this disambiguation title.
return 'nochange'
- else:
- # stop loop and save page
- break
+
+ # stop loop and save page
+ break
+
# Ensure that next time around we will not find this same hit.
curpos = m.start() + 1
try:
@@ -833,21 +834,27 @@
foundlink.parse()
except pywikibot.Error:
continue
+
# ignore interwiki links
if foundlink.site != disambPage.site:
continue
+
# Check whether the link found is to disambPage.
try:
if foundlink.canonical_title() != disambPage.title():
continue
+
except pywikibot.Error:
# must be a broken link
pywikibot.log('Invalid link [[%s]] in page [[%s]]'
% (m.group('title'), refPage.title()))
continue
+
n += 1
+
# how many bytes should be displayed around the current link
context = 60
+
# check if there's a dn-template here already
if (self.dnSkip and self.dn_template_str
and self.dn_template_str[:-2] in text[
@@ -868,6 +875,7 @@
StandardOption('next page', 'n'),
StandardOption('next disambig', 'g'),
StandardOption('unlink', 'u')]
+
if self.dn_template_str:
# '?', '/' for old choice
options += [AliasOption('tag template %s' %
@@ -877,6 +885,7 @@
if not edited:
options += [ShowPageOption('show disambiguation page', 'd',
m.start(), disambPage)]
+
options += [
OutputProxyOption('list', 'l',
SequenceOutputter(self.alternatives)),
@@ -892,22 +901,26 @@
if answer == 'x':
assert edited, 'invalid option before editing'
break
- elif answer == 's':
+
+ if answer == 's':
n -= 1 # TODO what's this for?
continue
- elif answer == 'e':
+
+ if answer == 'e':
text = edit.new_text
edited = True
curpos = 0
continue
- elif answer == 'n':
+
+ if answer == 'n':
# skip this page
if self.primary:
# If run with the -primary argument, skip this
# occurrence next time.
self.primaryIgnoreManager.ignore(refPage)
return 'nextpage'
- elif answer == 'g':
+
+ if answer == 'g':
return 'nextdisambig'
# The link looks like this:
@@ -918,13 +931,16 @@
if not link_text:
# or like this: [[page_title]]trailing_chars
link_text = page_title
+
if m.group('section') is None:
section = ''
else:
section = m.group('section')
+
trailing_chars = m.group('linktrail')
if trailing_chars:
link_text += trailing_chars
+
if answer == 't':
assert self.dn_template_str
# small chunk of text to search
@@ -932,71 +948,73 @@
# figure out where the link (and sentence) ends, put note
# there
end_of_word_match = re.search(r'\s', search_text)
+
if end_of_word_match:
position_split = end_of_word_match.start(0)
else:
position_split = 0
+
# insert dab needed template
text = (text[:m.end() + position_split]
+ self.dn_template_str
+ text[m.end() + position_split:])
dn = True
continue
- elif answer == 'u':
+
+ if answer == 'u':
# unlink - we remove the section if there's any
text = text[:m.start()] + link_text + text[m.end():]
unlink_counter += 1
continue
- else:
- # Check that no option from above was missed
- assert isinstance(answer, tuple), 'only tuple answer left.'
- assert answer[0] in ['r', ''], 'only valid tuple answers.'
- if answer[0] == 'r':
- # we want to throw away the original link text
- replaceit = link_text == page_title
- elif include == 'redirect':
- replaceit = True
- else:
- replaceit = False
- new_page_title = answer[1]
- repPl = pywikibot.Page(pywikibot.Link(new_page_title,
- disambPage.site))
- if (new_page_title[0].isupper()
- or link_text[0].isupper()):
- new_page_title = repPl.title()
- else:
- new_page_title = repPl.title()
- new_page_title = first_lower(new_page_title)
- if new_page_title not in new_targets:
- new_targets.append(new_page_title)
- if replaceit and trailing_chars:
- newlink = '[[{0}{1}]]{2}'.format(new_page_title,
- section,
- trailing_chars)
- elif replaceit or (new_page_title == link_text
- and not section):
- newlink = '[[{0}]]'.format(new_page_title)
- # check if we can create a link with trailing characters
- # instead of a pipelink
- elif (
- (len(new_page_title) <= len(link_text))
- and (firstcap(link_text[:len(new_page_title)])
- == firstcap(new_page_title))
- and (self.trailR.sub(
- '', link_text[len(new_page_title):]) == '')
- and (not section)
- ):
- newlink = '[[{0}]]{1}'.format(
- link_text[:len(new_page_title)],
- link_text[len(new_page_title):])
- else:
- newlink = '[[{0}{1}|{2}]]'.format(new_page_title,
- section, link_text)
- text = text[:m.start()] + newlink + text[m.end():]
- continue
- # Todo: This line is unreachable (T155337)
- pywikibot.output(text[max(0, m.start() - 30):m.end() + 30])
+ # else check that no option from above was missed
+ assert isinstance(answer, tuple), 'only tuple answer left.'
+ assert answer[0] in ['r', ''], 'only valid tuple answers.'
+
+ if answer[0] == 'r':
+ # we want to throw away the original link text
+ replaceit = link_text == page_title
+ else:
+ replaceit = include == 'redirect'
+
+ new_page_title = answer[1]
+ repPl = pywikibot.Page(pywikibot.Link(new_page_title,
+ disambPage.site))
+ if new_page_title[0].isupper() or link_text[0].isupper():
+ new_page_title = repPl.title()
+ else:
+ new_page_title = repPl.title()
+ new_page_title = first_lower(new_page_title)
+
+ if new_page_title not in new_targets:
+ new_targets.append(new_page_title)
+
+ if replaceit and trailing_chars:
+ newlink = '[[{0}{1}]]{2}'.format(new_page_title,
+ section,
+ trailing_chars)
+ elif replaceit or (new_page_title == link_text
+ and not section):
+ newlink = '[[{0}]]'.format(new_page_title)
+ # check if we can create a link with trailing characters
+ # instead of a pipelink
+ elif (
+ (len(new_page_title) <= len(link_text))
+ and (firstcap(link_text[:len(new_page_title)])
+ == firstcap(new_page_title))
+ and (self.trailR.sub(
+ '', link_text[len(new_page_title):]) == '')
+ and (not section)
+ ):
+ newlink = '[[{0}]]{1}'.format(
+ link_text[:len(new_page_title)],
+ link_text[len(new_page_title):])
+ else:
+ newlink = '[[{0}{1}|{2}]]'.format(new_page_title,
+ section, link_text)
+ text = text[:m.start()] + newlink + text[m.end():]
+ continue
+
if text == original_text:
pywikibot.output('\nNo changes have been made:\n')
else:
@@ -1012,6 +1030,7 @@
pywikibot.output('Page not saved: page is locked')
except pywikibot.PageNotSaved as error:
pywikibot.output('Page not saved: {0}'.format(error.args))
+
return 'done'
def findAlternatives(self, disambPage) -> bool:
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/638071
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: Idf92aa58f817b8c8f65404242919ee99476aa52e
Gerrit-Change-Number: 638071
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: D3r1ck01 <xsavitar.wiki(a)aol.com>
Gerrit-Reviewer: Hazard-SJ <hazardsjwiki(a)gmail.com>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/638422 )
Change subject: [IMPR] create a Site from sitename
......................................................................
[IMPR] create a Site from sitename
like
s = pywikibot.Site('wikipedia:test')
Change-Id: I0ee625bbc8ed7ccae45679e5b92b53b84d7f51dc
---
M pywikibot/__init__.py
1 file changed, 9 insertions(+), 2 deletions(-)
Approvals:
Hazard-SJ: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index e9f40c7..56252c1 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -1098,6 +1098,7 @@
using the method parameters.
@param code: language code (override config.mylang)
+ code may also be a sitename like 'wikipedia:test'
@param fam: family name or object (override config.family)
@type fam: str or pywikibot.family.Family
@param user: bot user name to use on this site (override config.usernames)
@@ -1123,13 +1124,19 @@
'URL to the wiki OR a pair of code and family name '
'should be provided')
code, fam = _code_fam_from_url(url)
+ elif code and ':' in code:
+ if fam:
+ raise ValueError(
+ 'sitename OR a pair of code and family name '
+ 'should be provided')
+ fam, _, code = code.partition(':')
else:
# Fallback to config defaults
code = code or config.mylang
fam = fam or config.family
- if not isinstance(fam, Family):
- fam = Family.load(fam)
+ if not isinstance(fam, Family):
+ fam = Family.load(fam)
interface = interface or fam.interface(code)
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/638422
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: I0ee625bbc8ed7ccae45679e5b92b53b84d7f51dc
Gerrit-Change-Number: 638422
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Hazard-SJ <hazardsjwiki(a)gmail.com>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/638414 )
Change subject: [IMPR] pywikibot.Site() parameters "interface" and "url" must be keyworded
......................................................................
[IMPR] pywikibot.Site() parameters "interface" and "url" must be keyworded
- For readability drop positional arguments for "interface", "url"
and deprecated "sysop"; they must be given as keyword arguments
Change-Id: Ie9f989cf92a5644e237bd3fa1c71da37011de966
---
M pywikibot/__init__.py
1 file changed, 2 insertions(+), 7 deletions(-)
Approvals:
Hazard-SJ: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index e9f40c7..008b223 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -58,7 +58,6 @@
from pywikibot.tools import (
classproperty,
deprecate_arg as _deprecate_arg,
- issue_deprecation_warning,
normalize_username,
MediaWikiVersion as _MediaWikiVersion,
ModuleDeprecationWrapper as _ModuleDeprecationWrapper,
@@ -1087,8 +1086,8 @@
@_deprecate_arg('sysop', None)
-def Site(code: Optional[str] = None, fam=None, user: Optional[str] = None,
- sysop=None, interface=None,
+def Site(code: Optional[str] = None, fam=None, user: Optional[str] = None, *,
+ interface=None,
url: Optional[str] = None) -> Union[APISite, DataSite, ClosedSite]:
"""A factory method to obtain a Site object.
@@ -1112,10 +1111,6 @@
"""
_logger = 'wiki'
- if sysop is not None:
- issue_deprecation_warning('positional argument of "sysop"', depth=3,
- warning_class=DeprecationWarning,
- since='20190907')
if url:
# Either code and fam or only url
if code or fam:
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/638414
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: Ie9f989cf92a5644e237bd3fa1c71da37011de966
Gerrit-Change-Number: 638414
Gerrit-PatchSet: 2
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Hazard-SJ <hazardsjwiki(a)gmail.com>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
Xqt has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/i18n/+/639165 )
Change subject: [tests] six is no longer required for tests
......................................................................
[tests] six is no longer required for tests
Change-Id: I51c1b22f3e9786662c6aa3658fdfd596a341d789
---
M .travis.yml
1 file changed, 1 insertion(+), 1 deletion(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/.travis.yml b/.travis.yml
index 02b6c15..8f98fb5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,7 +10,7 @@
- for item in *; do if [[ -d "$item" ]]; then cp -rp "$item" pywikibot-build/scripts/i18n; fi; done
install:
- - pip install six nose requests
+ - pip install nose requests
script:
- cd pywikibot-build
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/i18n/+/639165
To unsubscribe, or for help writing mail filters, visit https://gerrit.wikimedia.org/r/settings
Gerrit-Project: pywikibot/i18n
Gerrit-Branch: master
Gerrit-Change-Id: I51c1b22f3e9786662c6aa3658fdfd596a341d789
Gerrit-Change-Number: 639165
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/+/639151 )
Change subject: [bugfix] Fix TypeError in i18n.translate
......................................................................
[bugfix] Fix TypeError in i18n.translate
Change-Id: Ia3b68f9fc5e99a19c33c0555a45631a18b0c43d8
---
M pywikibot/i18n.py
1 file changed, 6 insertions(+), 6 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/pywikibot/i18n.py b/pywikibot/i18n.py
index 682c1d9..359fc96 100644
--- a/pywikibot/i18n.py
+++ b/pywikibot/i18n.py
@@ -29,7 +29,7 @@
from collections import defaultdict
from contextlib import suppress
from textwrap import fill
-from typing import Optional
+from typing import Optional, Union
from warnings import warn
import pywikibot
@@ -545,7 +545,10 @@
DEFAULT_FALLBACK = ('_default', )
-def translate(code, xdict, parameters=None, fallback=False):
+def translate(code,
+ xdict: Union[dict, str],
+ parameters: Union[dict, str, int, None] = None,
+ fallback=False) -> str:
"""Return the most appropriate localization from a localization dict.
Given a site code and a dictionary, returns the dictionary's value for
@@ -569,14 +572,11 @@
dictionary with family names as keys containing code dictionaries
or a single string. May contain PLURAL tags as described in
twtranslate
- @type xdict: dict, string, unicode
@param parameters: For passing (plural) parameters
- @type parameters: dict, string, unicode, int
@param fallback: Try an alternate language code. If it's iterable it'll
also try those entries and choose the first match.
@type fallback: boolean or iterable
@return: the localized string
- @rtype: str
@raise IndexError: If the language supports and requires more plurals
than defined for the given PLURAL pattern.
@raise KeyError: No fallback key found if fallback is not False
@@ -589,7 +589,7 @@
try:
lookup = xdict[code]
- except KeyError:
+ except (KeyError, TypeError):
# Check whether xdict has multiple projects
if isinstance(xdict, dict) and family in xdict:
lookup = xdict[family]
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/639151
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: Ia3b68f9fc5e99a19c33c0555a45631a18b0c43d8
Gerrit-Change-Number: 639151
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Isaacandy <isaac(a)iznd.xyz>
Gerrit-Reviewer: Siebrand <siebrand(a)kitano.nl>
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/+/606671 )
Change subject: [bugfix] Lookup the code parameter in xdict first
......................................................................
[bugfix] Lookup the code parameter in xdict first
i18n.py:
- Lookup the code parameter in xdict first (T255917)
- If code is not in xdict but the family name is found,
use xdict[family.name] to find a localization
- If fallback is False and no localization is found for a given family
always use 'wikipedia' entry if present even if there is a family entry
given. This family fallback prevents code duplication within xdict.
- The fallback lookup with 'wikipedia' key is made recursively with
only the xdict['wikipedia'] branch as actual parameter
- do not return directly if the fallback is false and no key is found.
This ensures the 'wikipedia' fallback is done
- update and extend translate method docstring
- update and extend package docstring
- ignore __iter__ and __len__ of _PluralMappingAliasfrom coverage
i18n_tests.py:
- rename TestTranslate to TestFallbackTranslate because these tests
have fallback enabled
- remove TestFallbackTranslate.setUp and use class attributes for
test dicts
- add a new TestTranslate test class to test extended xdict
- add a fake Site object which just holds code and family.name
attributes because the usage of pywikibot.Site is disabled by
DisableSiteMixin
Bug: T255917
Change-Id: I9bb6688064db7ac0050820889f78845aee4308d6
---
M pywikibot/i18n.py
M tests/i18n_tests.py
2 files changed, 206 insertions(+), 58 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/i18n.py b/pywikibot/i18n.py
index 6e89680..682c1d9 100644
--- a/pywikibot/i18n.py
+++ b/pywikibot/i18n.py
@@ -1,20 +1,19 @@
# -*- coding: utf-8 -*-
-"""
-Various i18n functions.
+"""Various i18n functions.
-Helper functions for both the internal translation system
-and for TranslateWiki-based translations.
+Helper functions for both the internal localization system and for
+TranslateWiki-based translations.
By default messages are assumed to reside in a package called
-'scripts.i18n'. In pywikibot 3+, that package is not packaged
-with pywikibot, and pywikibot 3+ does not have a hard dependency
-on any i18n messages. However, there are three user input questions
-in pagegenerators which will use i18n messages if they can be loaded.
+'scripts.i18n'. In pywikibot 3+, that package is not packaged with
+pywikibot, and pywikibot 3+ does not have a hard dependency on any i18n
+messages. However, there are three user input questions in pagegenerators
+which will use i18n messages if they can be loaded.
The default message location may be changed by calling
-L{set_message_package} with a package name. The package must contain
-an __init__.py, and a message bundle called 'pywikibot' containing
-messages. See L{twtranslate} for more information on the messages.
+L{set_message_package} with a package name. The package must contain an
+__init__.py, and a message bundle called 'pywikibot' containing messages.
+See L{twtranslate} for more information on the messages.
"""
#
# (C) Pywikibot team, 2004-2020
@@ -537,45 +536,50 @@
return self.source
def __iter__(self):
- raise NotImplementedError
+ raise NotImplementedError # pragma: no cover
def __len__(self):
- raise NotImplementedError
+ raise NotImplementedError # pragma: no cover
DEFAULT_FALLBACK = ('_default', )
def translate(code, xdict, parameters=None, fallback=False):
- """Return the most appropriate translation from a translation dict.
+ """Return the most appropriate localization from a localization dict.
- Given a language code and a dictionary, returns the dictionary's value for
+ Given a site code and a dictionary, returns the dictionary's value for
key 'code' if this key exists; otherwise tries to return a value for an
- alternative language that is most applicable to use on the wiki in
- language 'code' except fallback is False.
+ alternative code that is most applicable to use on the wiki in language
+ 'code' except fallback is False.
- The language itself is always checked first, then languages that
- have been defined to be alternatives, and finally English. If none of
- the options gives result, we just take the one language from xdict which
- may not be always the same. When fallback is iterable it'll return None if
- no code applies (instead of returning one).
+ The code itself is always checked first, then these codes that have
+ been defined to be alternatives, and finally English.
+
+ If fallback is False and the code is not found in the
For PLURAL support have a look at the twtranslate method.
- @param code: The language code
+ @param code: The site code as string or Site object. If xdict is an
+ extended dictionary the Site object should be used in favour of the
+ code string. Otherwise localizations from a wrong family might be
+ used.
@type code: str or Site object
- @param xdict: dictionary with language codes as keys or extended dictionary
- with family names as keys containing language dictionaries or
- a single (unicode) string. May contain PLURAL tags as
- described in twtranslate
+ @param xdict: dictionary with language codes as keys or extended
+ dictionary with family names as keys containing code dictionaries
+ or a single string. May contain PLURAL tags as described in
+ twtranslate
@type xdict: dict, string, unicode
@param parameters: For passing (plural) parameters
@type parameters: dict, string, unicode, int
@param fallback: Try an alternate language code. If it's iterable it'll
also try those entries and choose the first match.
@type fallback: boolean or iterable
- @raise IndexError: If the language supports and requires more plurals than
- defined for the given translation template.
+ @return: the localized string
+ @rtype: str
+ @raise IndexError: If the language supports and requires more plurals
+ than defined for the given PLURAL pattern.
+ @raise KeyError: No fallback key found if fallback is not False
"""
family = pywikibot.config.family
# If a site is given instead of a code, use its language
@@ -583,17 +587,19 @@
family = code.family.name
code = code.code
- # Check whether xdict has multiple projects
- if isinstance(xdict, dict):
- if family in xdict:
- xdict = xdict[family]
- elif 'wikipedia' in xdict:
- xdict = xdict['wikipedia']
+ try:
+ lookup = xdict[code]
+ except KeyError:
+ # Check whether xdict has multiple projects
+ if isinstance(xdict, dict) and family in xdict:
+ lookup = xdict[family]
+ else:
+ lookup = xdict
# Get the translated string
- if not isinstance(xdict, dict):
- trans = xdict
- elif not xdict:
+ if not isinstance(lookup, dict):
+ trans = lookup
+ elif not lookup:
trans = None
else:
codes = [code]
@@ -602,16 +608,22 @@
elif fallback is not False:
codes.extend(fallback)
for code in codes:
- if code in xdict:
- trans = xdict[code]
+ if code in lookup:
+ trans = lookup[code]
break
else:
- if fallback is False:
- return None
- raise KeyError('No fallback key found in lookup dict for "{}"'
- .format(code))
+ if fallback is not False:
+ raise KeyError('No fallback key found in lookup dict for "{}"'
+ .format(code))
+ trans = None
+
if trans is None:
+ if 'wikipedia' in xdict:
+ # fallback to wikipedia family
+ return translate(code, xdict['wikipedia'],
+ parameters=parameters, fallback=fallback)
return None # return None if we have no translation found
+
if parameters is None:
return trans
@@ -629,7 +641,7 @@
# On error: parameter is for PLURAL variants only,
# don't change the string
with suppress(KeyError, TypeError):
- return trans % parameters
+ trans = trans % parameters
return trans
diff --git a/tests/i18n_tests.py b/tests/i18n_tests.py
index 9b4c515..2cb6e7e 100644
--- a/tests/i18n_tests.py
+++ b/tests/i18n_tests.py
@@ -9,30 +9,166 @@
import pywikibot
-from pywikibot import i18n, bot, plural
+from pywikibot import bot, i18n, plural
from tests.aspects import (
- unittest, TestCase, DefaultSiteTestCase, PwbTestCase,
AutoDeprecationTestCase,
+ DefaultSiteTestCase,
+ PwbTestCase,
+ TestCase,
+ unittest,
)
+class Site:
+
+ """An object holding code and family, duck typing a pywikibot Site."""
+
+ class Family:
+
+ """Nested class to hold the family name attribute."""
+
+ pass
+
+ def __init__(self, code, family='wikipedia'):
+ """Initializer."""
+ self.code = code
+ self.family = self.Family()
+ setattr(self.family, 'name', family)
+
+ def __repr__(self):
+ return "'{site.family.name}:{site.code}'".format(site=self)
+
+
class TestTranslate(TestCase):
- """Test translate method."""
+ """Test translate method with fallback True."""
net = False
- def setUp(self):
- """Set up test method."""
- self.msg_localized = {'en': 'test-localized EN',
- 'nl': 'test-localized NL',
- 'fy': 'test-localized FY'}
- self.msg_semi_localized = {'en': 'test-semi-localized EN',
- 'nl': 'test-semi-localized NL'}
- self.msg_non_localized = {'en': 'test-non-localized EN'}
- self.msg_no_english = {'ja': 'test-no-english JA'}
- super().setUp()
+ xdict = {
+ 'en': 'test-localized EN',
+ 'commons': 'test-localized COMMONS',
+ 'wikipedia': {
+ 'nl': 'test-localized WP-NL',
+ 'fy': 'test-localized WP-FY',
+ 'wikipedia': { # test a deeply nested xdict
+ 'de': 'test-localized WP-DE',
+ },
+ },
+ 'wikisource': {
+ 'en': 'test-localized WS-EN',
+ 'fy': 'test-localized WS-FY',
+ 'ja': 'test-localized WS-JA',
+ },
+ }
+
+ def test_translate_commons(self):
+ """Test localization with xdict for commons.
+
+ Test whether the localzation is found either with the Site object
+ or with the site code.
+ """
+ site = Site('commons', 'commons')
+ for code in (site, 'commons'):
+ with self.subTest(code=code):
+ self.assertEqual(i18n.translate(code, self.xdict),
+ 'test-localized COMMONS')
+
+ def test_translate_de(self):
+ """Test localization fallbacks for 'de' with xdict.
+
+ 'de' key is defined in a nested 'wikipedia' sub dict. This should
+ always fall back to this nested 'wikipedia' entry.
+ """
+ site1 = Site('de', 'wikipedia')
+ site2 = Site('de', 'wikibooks')
+ site3 = Site('de', 'wikisource')
+ for code in (site1, site2, site3, 'de'):
+ with self.subTest(code=code):
+ self.assertEqual(i18n.translate(code, self.xdict),
+ 'test-localized WP-DE')
+
+ def test_translate_en(self):
+ """Test localization fallbacks for 'en' with xdict.
+
+ 'en' key is defined directly in xdict. This topmost key goes over
+ site specific key. Therefore 'test-localized WS-EN' is not given
+ back.
+ """
+ site1 = Site('en', 'wikipedia')
+ site2 = Site('en', 'wikibooks')
+ site3 = Site('en', 'wikisource')
+ for code in (site1, site2, site3, 'en'):
+ with self.subTest(code=code):
+ self.assertEqual(i18n.translate(code, self.xdict),
+ 'test-localized EN')
+
+ def test_translate_fy(self):
+ """Test localization fallbacks for 'fy' with xdict.
+
+ 'fy' key is defined in 'wikipedia' and 'wikisource' sub dicts.
+ They should have different localizations for these two families but
+ 'wikisource' should have a fallback to the 'wikipedia' entry.
+
+ Note: If the translate code is given as string, the result depends
+ on the current config.family entry. Therefore there is no test with
+ the code given as string.
+ """
+ site1 = Site('fy', 'wikipedia')
+ site2 = Site('fy', 'wikibooks')
+ site3 = Site('fy', 'wikisource')
+ for code in (site1, site2):
+ with self.subTest(code=code):
+ self.assertEqual(i18n.translate(code, self.xdict),
+ 'test-localized WP-FY')
+ self.assertEqual(i18n.translate(site3, self.xdict),
+ 'test-localized WS-FY')
+
+ def test_translate_nl(self):
+ """Test localization fallbacks for 'nl' with xdict.
+
+ 'nl' key is defined in 'wikipedia' sub dict. Therefore all
+ localizations have a fallback to the 'wikipedia' entry.
+ """
+ site1 = Site('nl', 'wikipedia')
+ site2 = Site('nl', 'wikibooks')
+ site3 = Site('nl', 'wikisource')
+ for code in (site1, site2, site3, 'nl'):
+ with self.subTest(code=code):
+ self.assertEqual(i18n.translate(code, self.xdict),
+ 'test-localized WP-NL')
+
+ def test_translate_ja(self):
+ """Test localization fallbacks for 'ja' with xdict.
+
+ 'ja' key is defined in 'wkisource' sub dict only. Therefore there
+ is no fallback to the 'wikipedia' entry and the localization result
+ is None.
+ """
+ site1 = Site('ja', 'wikipedia')
+ site2 = Site('ja', 'wikibooks')
+ site3 = Site('ja', 'wikisource')
+ for code in (site1, site2):
+ with self.subTest(code=code):
+ self.assertIsNone(i18n.translate(code, self.xdict))
+ self.assertEqual(i18n.translate(site3, self.xdict),
+ 'test-localized WS-JA')
+
+
+class TestFallbackTranslate(TestCase):
+
+ """Test translate method with fallback True."""
+
+ net = False
+
+ msg_localized = {'en': 'test-localized EN',
+ 'nl': 'test-localized NL',
+ 'fy': 'test-localized FY'}
+ msg_semi_localized = {'en': 'test-semi-localized EN',
+ 'nl': 'test-semi-localized NL'}
+ msg_non_localized = {'en': 'test-non-localized EN'}
+ msg_no_english = {'ja': 'test-no-english JA'}
def test_localized(self):
"""Test fully localized translations."""
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/606671
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: I9bb6688064db7ac0050820889f78845aee4308d6
Gerrit-Change-Number: 606671
Gerrit-PatchSet: 11
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Dvorapa <dvorapa(a)seznam.cz>
Gerrit-Reviewer: Isaacandy <isaac(a)iznd.xyz>
Gerrit-Reviewer: Siebrand <siebrand(a)kitano.nl>
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/+/618224 )
Change subject: [cleanup] Remove interwiki_forwarded_from list from family files
......................................................................
[cleanup] Remove interwiki_forwarded_from list from family files
interwiki_forwarded_from was implemented in compat but that feature wasn't
merged into the already existing core titletranslate.py. As a result,
interwiki_forwarded_from is unimplemented in core.
titletranslate is used by interwiki.py only which is used very rare
(is it still used?). Nobody missed this feature for so many years
and it can be removed then.
Bug: T104125
Change-Id: I5bd27becb735c2ecd817a3c27490dfeeca0fa948
---
M pywikibot/families/wikipedia_family.py
M pywikibot/family.py
2 files changed, 0 insertions(+), 18 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/families/wikipedia_family.py b/pywikibot/families/wikipedia_family.py
index 6cb6994..e4c51b4 100644
--- a/pywikibot/families/wikipedia_family.py
+++ b/pywikibot/families/wikipedia_family.py
@@ -110,18 +110,6 @@
'zh-yue': ('分類彈去',),
}
- # families that redirect their interlanguage links here.
- interwiki_forwarded_from = [
- 'commons',
- 'incubator',
- 'mediawiki',
- 'meta',
- 'outreach',
- 'species',
- 'test',
- 'wikimania'
- ]
-
# Global bot allowed languages on
# https://meta.wikimedia.org/wiki/BPI#Current_implementation
# & https://meta.wikimedia.org/wiki/Special:WikiSets/2
diff --git a/pywikibot/family.py b/pywikibot/family.py
index b46f381..49066c1 100644
--- a/pywikibot/family.py
+++ b/pywikibot/family.py
@@ -535,12 +535,6 @@
# family.
interwiki_forward = None
- # Some families, e. g. wikipedia, receive forwarded interlanguage
- # links from other families, e. g. incubator, commons, or meta.
- # These families can set this variable to the names of their source
- # families.
- interwiki_forwarded_from = {}
-
# Which language codes no longer exist and by which language code
# should they be replaced. If for example the language with code xx:
# now should get code yy:, add {'xx':'yy'} to obsolete.
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/618224
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: I5bd27becb735c2ecd817a3c27490dfeeca0fa948
Gerrit-Change-Number: 618224
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Huji <huji.huji(a)gmail.com>
Gerrit-Reviewer: Mpaa <mpaa.wiki(a)gmail.com>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged