jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1081490?usp=email )
Change subject: [doc] use options list with login script
......................................................................
[doc] use options list with login script
Change-Id: Iec9f5942fa022ef88bd30cbcd17b85aa5c99d178
---
M pywikibot/scripts/login.py
1 file changed, 21 insertions(+), 24 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/scripts/login.py b/pywikibot/scripts/login.py
index 83be030..ce3f29f 100755
--- a/pywikibot/scripts/login.py
+++ b/pywikibot/scripts/login.py
@@ -1,44 +1,41 @@
#!/usr/bin/env python3
-"""
-Script to log the bot in to a wiki account.
+"""Script to log the bot in to a wiki account.
Suggestion is to make a special account to use for bot use only. Make
sure this bot account is well known on your home wiki before using.
-The following parameters are supported::
+The following parameters are supported:
- -family:FF Log in to the LL language of the FF family.
- -lang:LL Example: -family:wiktionary -lang:fr will log you in at
- fr.wiktionary.org.
+-all Try to log in on all sites where a username is defined in
+ user config file (user-config.py).
- -site:FF:LL Log in to the LL language of the FF family
+-logout Log out of the current site. Combine with ``-all`` to log
+ out of all sites, or with :ref:`global options` ``-family``,
+ ``-lang`` or ``-site`` to log out of a specific site.
- -all Try to log in on all sites where a username is defined in
- user config file (user-config.py).
+-oauth Generate OAuth authentication information.
- -logout Log out of the current site. Combine with -all to log out of
- all sites, or with -family and -lang to log out of a specific
- site.
+ .. note:: Need to copy OAuth tokens to your user config
+ file manually. -logout is not compatible with -oauth.
- -oauth Generate OAuth authentication information.
+-autocreate Auto-create an account using unified login when necessary.
- .. note:: Need to copy OAuth tokens to your user config
- file manually. -logout is not compatible with -oauth.
+ .. note:: the global account must exist already before
+ using this.
- -autocreate Auto-create an account using unified login when necessary.
+-async Run the bot in parallel tasks, only usefull together with
+ ``-all`` option
- .. note:: the global account must exist already before
- using this.
-
- -async Run the bot in parallel tasks
+.. hint:: Use :ref:`global options` ``-code``, ``-family`` or ``-site``
+ to determine the site to login/logout.
If not given as parameter, the script will ask for your username and
password (password entry will be hidden), log in to your home wiki using
-this combination, and store the resulting cookies (containing your password
-hash, so keep it secured!) in a file in the data subdirectory.
+this combination, and store the resulting cookies (containing your
+password hash, so keep it secured!) in a file in the data subdirectory.
-All scripts in this library will be looking for this cookie file and will
-use the login information if it is present.
+All scripts in this library will be looking for this cookie file and
+will use the login information if it is present.
To log out, throw away the ``*.lwp`` file that is created in the data
subdirectory.
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1081490?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: Iec9f5942fa022ef88bd30cbcd17b85aa5c99d178
Gerrit-Change-Number: 1081490
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1081475?usp=email )
Change subject: [fix] no longer raise UnsupportedPageError within PageGenerator.result()
......................................................................
[fix] no longer raise UnsupportedPageError within PageGenerator.result()
Bug: T377651
Change-Id: I6f55ca09c55f3c3fd0368a59be2bf0d5164aa9fb
---
M pywikibot/data/api/_generators.py
M tests/page_tests.py
2 files changed, 14 insertions(+), 1 deletion(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/data/api/_generators.py b/pywikibot/data/api/_generators.py
index 69ca560..6f1206d 100644
--- a/pywikibot/data/api/_generators.py
+++ b/pywikibot/data/api/_generators.py
@@ -727,6 +727,10 @@
This can be overridden in subclasses to return a different type
of object.
+ .. versionchanged:: 9.4
+ no longer raise :exc:`exceptions.UnsupportedPageError` but
+ return a generic :class:`pywikibot.Page` obect. The exception
+ is raised when getting the content for example.
"""
p = pywikibot.Page(self.site, pagedata['title'], pagedata['ns'])
ns = pagedata['ns']
@@ -738,7 +742,10 @@
p = pywikibot.FilePage(p)
elif ns == Namespace.CATEGORY:
p = pywikibot.Category(p)
- update_page(p, pagedata, self.props)
+
+ with suppress(UnsupportedPageError):
+ update_page(p, pagedata, self.props)
+
return p
diff --git a/tests/page_tests.py b/tests/page_tests.py
index 0651e34..716bb8c 100755
--- a/tests/page_tests.py
+++ b/tests/page_tests.py
@@ -853,6 +853,12 @@
self.assertFalse(page.botMayEdit())
del page
+ def test_real_page(self):
+ """Test botMayEdit for real example due to T377651."""
+ site = pywikibot.Site('wikipedia:en')
+ page = pywikibot.Page(site, 'Talk:Alan Turing')
+ self.assertTrue(page.botMayEdit())
+
class TestPageHistory(DefaultSiteTestCase):
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1081475?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: I6f55ca09c55f3c3fd0368a59be2bf0d5164aa9fb
Gerrit-Change-Number: 1081475
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1081477?usp=email )
Change subject: [doc] fix docstring in /data/superset.py
......................................................................
[doc] fix docstring in /data/superset.py
Change-Id: Ia5b5a2c30f928889c68e7958e43ab8899053b53e
---
M pywikibot/data/superset.py
1 file changed, 2 insertions(+), 2 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/pywikibot/data/superset.py b/pywikibot/data/superset.py
index aabcfa2..6e223df 100644
--- a/pywikibot/data/superset.py
+++ b/pywikibot/data/superset.py
@@ -114,7 +114,7 @@
instance is not connected, it attempts to log in first.
:raises ServerError: For any http errors
- :return CSRF token string
+ :return: CSRF token string
"""
if not self.connected:
self.login()
@@ -172,7 +172,7 @@
:raises TypeError: if site and schema_name are both defined'
:raises TypeError: If determined database_id is not an integer.
:raises TypeError: If neither site nor schema_name is determined.
- :return A tuple containing database_id and schema_name.
+ :return: A tuple containing database_id and schema_name.
"""
if site and schema_name:
msg = 'Only one of schema_name and site parameters can be defined'
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1081477?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: Ia5b5a2c30f928889c68e7958e43ab8899053b53e
Gerrit-Change-Number: 1081477
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1081397?usp=email )
Change subject: [tests] update coverage
......................................................................
[tests] update coverage
Change-Id: Icd41894db54a0ed067f5ff2c3370ca2cc69ee2b9
---
M tests/__init__.py
M tests/aspects.py
2 files changed, 50 insertions(+), 55 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/tests/__init__.py b/tests/__init__.py
index 00e5dbf..8eb9a7a 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -217,7 +217,7 @@
'Disabled test modules (to run: python -m unittest ...):\n {}'
.format(', '.join(disabled_test_modules)))
- if extra_test_modules:
+ if extra_test_modules: # pragma: no cover
unittest_print(
'Extra test modules (run after library, before scripts):\n {}'
.format(', '.join(extra_test_modules)))
diff --git a/tests/aspects.py b/tests/aspects.py
index 67d5bad..7a6da4e 100644
--- a/tests/aspects.py
+++ b/tests/aspects.py
@@ -120,14 +120,12 @@
msg, f'len({safe_repr(seq)}): {first_len} != {second_len}')
self.fail(msg)
- def assertPageInNamespaces(self, page, namespaces):
- """
- Assert that Pages is in namespaces.
+ def assertPageInNamespaces(self, page, namespaces: int | set[int]) -> None:
+ """Assert that Pages is in namespaces.
:param page: Page
:type page: pywikibot.BasePage
:param namespaces: expected namespaces
- :type namespaces: int or set of int
"""
if isinstance(namespaces, int):
namespaces = {namespaces}
@@ -170,18 +168,13 @@
titles = list(titles)
return titles
- def assertPagesInNamespaces(self, gen, namespaces):
- """
- Assert that generator returns Pages all in namespaces.
+ def assertPagesInNamespaces(self, gen, namespaces: int | set[int]) -> None:
+ """Assert that generator returns Pages all in namespaces.
:param gen: generator to iterate
:type gen: generator
:param namespaces: expected namespaces
- :type namespaces: int or set of int
"""
- if isinstance(namespaces, int):
- namespaces = {namespaces}
-
for page in gen:
self.assertPageInNamespaces(page, namespaces)
@@ -200,8 +193,8 @@
:raises TypeError: Invalid *namespaces* type
"""
if isinstance(namespaces, int):
- namespaces = {namespaces}
- elif not isinstance(namespaces, set):
+ namespaces = {namespaces} # pragma: no cover
+ elif not isinstance(namespaces, set): # pragma: no cover
raise TypeError('namespaces argument must be an int or a set, not '
f'{type(namespaces).__name__}')
@@ -282,12 +275,13 @@
for required_module in required_modules:
try:
__import__(required_module, globals(), locals(), [], 0)
- except ModuleNotFoundError:
+ except ModuleNotFoundError: # pragma: no cover
missing += [required_module]
if not missing:
return obj
- skip_decorator = unittest.skip(f"{', '.join(missing)} not installed")
- return skip_decorator(obj)
+ skip_decorator = unittest.skip( # pragma: no cover
+ f"{', '.join(missing)} not installed")
+ return skip_decorator(obj) # pragma: no cover
return test_requirement
@@ -317,17 +311,17 @@
"""Validate environment."""
if not isinstance(self.site, BaseSite) \
or isinstance(self.site, DrySite):
- raise Exception(
+ raise Exception( # pragma: no cover
f'{type(self).__name__}.site must be a BaseSite not '
f'{type(self.site).__name__}.')
if args or kwargs:
- raise Exception(
+ raise Exception( # pragma: no cover
f'Test method {method.__name__!r} has parameters which is '
f'not supported with require_version decorator.')
_, op, version = re.split('([<>]=?)', version_needed)
- if not op:
+ if not op: # pragma: no cover
raise Exception(f'There is no valid operator given with '
f'version {version_needed!r}')
@@ -336,7 +330,7 @@
if not skip:
return method(self, *args, **kwargs)
- myreason = ' to ' + reason if reason else ''
+ myreason = ' to ' + reason if reason else '' # pragma: no cover
raise unittest.SkipTest(
f'MediaWiki {op} v{version} required{myreason}.')
@@ -396,7 +390,7 @@
def __init__(self, code, fam=None, user=None):
"""Initializer."""
- raise SiteDefinitionError(
+ raise SiteDefinitionError( # pragma: no cover
f'Loading site {fam}:{code} during dry test not permitted')
@@ -452,11 +446,11 @@
super().setUpClass()
if not hasattr(cls, 'sites'):
- return
+ return # pragma: no cover
for key, data in cls.sites.items():
if 'hostname' not in data:
- raise Exception(
+ raise Exception( # pragma: no cover
f'{cls.__name__}: hostname not defined for {key}')
hostname = data['hostname']
@@ -522,7 +516,7 @@
:raises Exception: test class cannot use *write* attribute
together with *cached* and must be run on test sites only.
"""
- if issubclass(cls, ForceCacheMixin):
+ if issubclass(cls, ForceCacheMixin): # pragma: no cover
raise Exception(f'{cls.__name__} cannot be a subclass of both'
' SiteWriteMixin and ForceCacheMixin')
@@ -535,7 +529,7 @@
if (not hasattr(site.family, 'test_codes')
or site.code not in site.family.test_codes):
- raise Exception(
+ raise Exception( # pragma: no cover
f'{cls.__name__} should only be run on test sites. To run '
f'this test, add {site.code!r} to the {site.family.name}'
" family attribute 'test_codes'."
@@ -631,7 +625,7 @@
# For multi-site test classes, or site is specified as a param,
# the cached userpage object may not be the desired site.
if hasattr(self, '_userpage') and self._userpage.site == site:
- return self._userpage
+ return self._userpage # pragma: no cover
userpage = pywikibot.User(site, site.username())
self._userpage = userpage
@@ -761,7 +755,7 @@
if hostnames:
dct.setdefault('sites', {})
for hostname in hostnames:
- if hostname in dct['sites']:
+ if hostname in dct['sites']: # pragma: no cover
raise AttributeError(f'hostname {hostname!r} already found'
f"in dict['sites']:\n{dict['sites']}")
dct['sites'][hostname] = {'hostname': hostname}
@@ -779,7 +773,7 @@
# test writer explicitly sets 'site=False' so code reviewers
# check that the script invoked by pwb will not load a site.
if dct.get('pwb') and 'site' not in dct:
- raise Exception(
+ raise Exception( # pragma: no cover
f'{name}: Test classes using pwb must set "site";'
' add site=False if the test script will not use a site'
)
@@ -790,7 +784,7 @@
del dct['site']
# If there isn't a site, require declaration of net activity.
- if 'net' not in dct:
+ if 'net' not in dct: # pragma: no cover
raise Exception(f'{name}: Test classes without a site'
' configured must set "net"')
@@ -814,7 +808,7 @@
if dct.get('net'):
bases = cls.add_base(bases, CheckHostnameMixin)
- elif hostnames:
+ elif hostnames: # pragma: no cover
raise Exception('"net" must be True with hostnames defined')
if dct.get('write'):
@@ -843,7 +837,7 @@
# A multi-site test method only accepts 'self' and the site-key
if test_func.__code__.co_argcount != 2:
- raise Exception(
+ raise Exception( # pragma: no cover
f'{name}: Test method {test} must accept either 1 or 2 '
f'arguments; {test_func.__code__.co_argcount} found'
)
@@ -884,7 +878,7 @@
if doc_suffix:
if not doc:
doc = method.__doc__
- if doc[-1] != '.':
+ if doc[-1] != '.': # pragma: no cover
raise ValueError('doc string must end with a period.')
doc = doc[:-1] + ' ' + doc_suffix + '.'
@@ -911,7 +905,7 @@
# This stores the site under the site name.
if not cls.sites:
- cls.sites = {}
+ cls.sites = {} # pragma: no cover
# If the test is not cached, create new Site objects for this class
cm = cls._uncached()
@@ -964,17 +958,17 @@
*name*.
"""
if not name and hasattr(cls, 'sites'):
- if len(cls.sites) != 1:
+ if len(cls.sites) != 1: # pragma: no cover
raise Exception(f'"{cls.__name__}.get_site(name=None)"'
' called with multiple sites')
name = next(iter(cls.sites.keys()))
- if name and name not in cls.sites:
+ if name and name not in cls.sites: # pragma: no cover
raise Exception(f'"{name}" not declared in {cls.__name__}')
if isinstance(cls.site, BaseSite):
- if cls.sites[name]['site'] != cls.site:
+ if cls.sites[name]['site'] != cls.site: # pragma: no cover
raise Exception(f'{cls.__name__}.site is different from '
f"{cls.__name__}.sites[{name!r}]['site']:\n"
f"{cls.site} != {cls.sites[name]['site']}")
@@ -985,9 +979,9 @@
@classmethod
def has_site_user(cls, family, code):
"""Check the user config has a user for the site."""
- if not family:
+ if not family: # pragma: no cover
raise Exception(f'no family defined for {cls.__name__}')
- if not code:
+ if not code: # pragma: no cover
raise Exception(f'no site code defined for {cls.__name__}')
usernames = config.usernames
@@ -1027,7 +1021,7 @@
maintitle = removeprefix(maintitle, 'Special:MyLanguage/') # T278702
mainpage = pywikibot.Page(site, maintitle)
if not isinstance(site, DrySite) and mainpage.isRedirectPage():
- mainpage = mainpage.getRedirectTarget()
+ mainpage = mainpage.getRedirectTarget() # pragma: no cover
if force:
mainpage = pywikibot.Page(self.site, mainpage.title())
@@ -1189,7 +1183,7 @@
super().setUpClass()
if not (hasattr(cls, 'site') and hasattr(cls, 'sites')) \
- or len(cls.sites) != 1:
+ or len(cls.sites) != 1: # pragma: no cover
raise Exception('"site" or "sites" attribute is missing or "sites"'
'entries count is different from 1')
@@ -1218,7 +1212,7 @@
with cls._uncached():
for data in cls.sites.values():
if 'site' not in data:
- continue
+ continue # pragma: no cover
site = data['site']
if not site.has_data_repository:
@@ -1227,8 +1221,9 @@
if (hasattr(cls, 'repo')
and cls.repo != site.data_repository()):
- raise Exception(f'{cls.__name__}: sites do not all have'
- ' the same data repository')
+ raise Exception( # pragma: no cover
+ f'{cls.__name__}: sites do not all have the same data'
+ ' repository')
cls.repo = site.data_repository()
@@ -1327,7 +1322,7 @@
"""Prepare the environment for running the pwb.py script."""
super().setUp()
self.orig_pywikibot_dir = None
- if 'PYWIKIBOT_DIR' in os.environ:
+ if 'PYWIKIBOT_DIR' in os.environ: # pragma: no cover
self.orig_pywikibot_dir = os.environ['PYWIKIBOT_DIR']
base_dir = pywikibot.config.base_dir
os.environ['PYWIKIBOT_DIR'] = base_dir
@@ -1336,7 +1331,7 @@
"""Restore the environment after running the pwb.py script."""
super().tearDown()
del os.environ['PYWIKIBOT_DIR']
- if self.orig_pywikibot_dir:
+ if self.orig_pywikibot_dir: # pragma: no cover
os.environ['PYWIKIBOT_DIR'] = self.orig_pywikibot_dir
def execute(self, args: list[str], **kwargs):
@@ -1405,7 +1400,7 @@
self.warning_log = []
self.expect_warning_filename = inspect.getfile(self.__class__)
- if self.expect_warning_filename.endswith('.pyc'):
+ if self.expect_warning_filename.endswith('.pyc'): # pragma: no cover
self.expect_warning_filename = self.expect_warning_filename[:-1]
self._do_test_warning_filename = True
@@ -1439,14 +1434,14 @@
msg = f'{deprecated} is deprecated'
if instead:
msg += f'; use {instead} instead.'
- elif instead is None:
+ elif instead is None: # pragma: no cover
msg = None
- elif instead is True:
+ elif instead is True: # pragma: no cover
msg = cls.INSTEAD
- elif instead is False:
+ elif instead is False: # pragma: no cover
msg = cls.NO_INSTEAD
else:
- raise TypeError(
+ raise TypeError( # pragma: no cover
f'instead argument must not be a {type(instead).__name__!r}')
return msg
@@ -1485,7 +1480,7 @@
if (match and bool(match[1]) == (msg is self.INSTEAD)
or msg is None):
break
- else:
+ else: # pragma: no cover
self.fail('No generic deprecation message match found in '
f'{deprecation_messages}')
else:
@@ -1494,7 +1489,7 @@
if message.startswith(head) \
and message.endswith(tail):
break
- else:
+ else: # pragma: no cover
self.fail(f"'{msg}' not found in {self.deprecation_messages}"
'(ignoring since)')
if self._do_test_warning_filename:
@@ -1522,7 +1517,7 @@
def assertNoDeprecation(self, msg=None):
"""Assert that no deprecation warning happened."""
- if msg:
+ if msg: # pragma: no cover
self.assertNotIn(msg, self.deprecation_messages)
else:
self.assertIsEmpty(self.deprecation_messages)
@@ -1537,7 +1532,7 @@
for item in self.warning_log:
if (self._ignore_unknown_warning_packages
and 'pywikibot' not in item.filename):
- continue
+ continue # pragma: no cover
if item.filename != filename:
self.fail(f'expected warning filename {filename}; warning '
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1081397?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: Icd41894db54a0ed067f5ff2c3370ca2cc69ee2b9
Gerrit-Change-Number: 1081397
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot
jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1079658?usp=email )
Change subject: [tests] remove `PYWIKIBOT_TEST_WRITE_FAIL` from documentation
......................................................................
[tests] remove `PYWIKIBOT_TEST_WRITE_FAIL` from documentation
Change-Id: Ieec6c02cc3e22925647049750cc87e02c256935e
---
M tests/README.rst
1 file changed, 5 insertions(+), 15 deletions(-)
Approvals:
jenkins-bot: Verified
Xqt: Looks good to me, approved
diff --git a/tests/README.rst b/tests/README.rst
index 53a0784..7c82899 100644
--- a/tests/README.rst
+++ b/tests/README.rst
@@ -145,18 +145,11 @@
PYWIKIBOT_TEST_WRITE=1
-**PYWIKIBOT_TEST_WRITE_FAIL**
- There are a set of 'edit failure' tests, which attempt to write to the wikis
- and **should** fail. If there is a bug in pywikibot or MediaWiki, these
- tests **may** actually perform a write operation.
-
- To enable 'edit failure' tests, set::
-
- PYWIKIBOT_TEST_WRITE_FAIL=1
-
- .. deprecated:: 9.2
- this environment variable no longer has any effect; use
- :envvar:`PYWIKIBOT_TEST_WRITE` instead.
+.. versionremoved:: 9.2
+ The :envvar:`PYWIKIBOT_TEST_WRITE_FAIL` environment variable; use
+ :envvar:`PYWIKIBOT_TEST_WRITE` instead.
+.. versionremoved:: 9.5
+ The :envvar:`PYWIKIBOT_TEST_GUI` environment variable.
Instead of setting the environment by the os (or `os.environ` as well) you can use the :mod:`pwb`
wrapper script to set it::
@@ -167,9 +160,6 @@
pwb PYWIKIBOT_TEST_WRITE script_tests -v TestScriptSimulate.test_archivebot
-.. versionremoved:: 9.5
- The :envvar:`PYWIKIBOT_TEST_GUI` environment variable.
-
Decorators
==========
--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1079658?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: Ieec6c02cc3e22925647049750cc87e02c256935e
Gerrit-Change-Number: 1079658
Gerrit-PatchSet: 1
Gerrit-Owner: Xqt <info(a)gno.de>
Gerrit-Reviewer: Xqt <info(a)gno.de>
Gerrit-Reviewer: jenkins-bot