jenkins-bot submitted this change.

View Change

Approvals: JJMC89: Looks good to me, approved jenkins-bot: Verified
automatically convert type comments to annotations

Change-Id: Icf47dbe653f015c4819251501e1c0a11439181c6
---
M pywikibot/__init__.py
M pywikibot/bot.py
M pywikibot/bot_choice.py
M pywikibot/config.py
M pywikibot/cosmetic_changes.py
M pywikibot/data/api/_requests.py
M pywikibot/date.py
M pywikibot/diff.py
M pywikibot/echo.py
M pywikibot/family.py
M pywikibot/flow.py
M pywikibot/i18n.py
M pywikibot/interwiki_graph.py
M pywikibot/logentries.py
M pywikibot/logging.py
M pywikibot/login.py
M pywikibot/page/_wikibase.py
M pywikibot/pagegenerators/__init__.py
M pywikibot/pagegenerators/_factory.py
M pywikibot/pagegenerators/_generators.py
M pywikibot/plural.py
M pywikibot/proofreadpage.py
M pywikibot/scripts/generate_user_files.py
M pywikibot/site/_apisite.py
M scripts/add_text.py
M scripts/blockpageschecker.py
M scripts/category.py
M scripts/redirect.py
M scripts/replace.py
M scripts/weblinkchecker.py
M scripts/welcome.py
M tests/__init__.py
32 files changed, 150 insertions(+), 146 deletions(-)

diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py
index ca3c648..3b5a311 100644
--- a/pywikibot/__init__.py
+++ b/pywikibot/__init__.py
@@ -92,7 +92,7 @@
# argvu is set by pywikibot.bot when it's imported

if not hasattr(sys.modules[__name__], 'argvu'):
- argvu = [] # type: List[str]
+ argvu: List[str] = []


class Coordinate(_WbRepresentation):
@@ -609,7 +609,8 @@
self.upperBound = self.lowerBound = None
else:
if error is None:
- upperError = lowerError = Decimal(0) # type: Optional[Decimal]
+ upperError: Optional[Decimal] = Decimal(0)
+ lowerError: Optional[Decimal] = Decimal(0)
elif isinstance(error, tuple):
upperError = self._todecimal(error[0])
lowerError = self._todecimal(error[1])
@@ -952,7 +953,7 @@
return cls(data)


-_sites = {} # type: Dict[str, APISite]
+_sites: Dict[str, APISite] = {}


@cache
@@ -1260,9 +1261,9 @@


# queue to hold pending requests
-page_put_queue = Queue(_config.max_queue_size) # type: Queue
+page_put_queue: Queue = Queue(_config.max_queue_size)
# queue to signal that async_manager is working on a request. See T147178.
-page_put_queue_busy = Queue(_config.max_queue_size) # type: Queue
+page_put_queue_busy: Queue = Queue(_config.max_queue_size)
# set up the background thread
_putthread = threading.Thread(target=async_manager,
name='Put-Thread', # for debugging purposes
diff --git a/pywikibot/bot.py b/pywikibot/bot.py
index be0d959..dbc4b2d83 100644
--- a/pywikibot/bot.py
+++ b/pywikibot/bot.py
@@ -269,7 +269,7 @@

"""

-ui = None # type: Optional[pywikibot.userinterfaces._interface_base.ABUIC]
+ui: Optional[pywikibot.userinterfaces._interface_base.ABUIC] = None
"""Holds a user interface object defined in
:mod:`pywikibot.userinterfaces` subpackage.
"""
@@ -706,7 +706,7 @@
Tuple[int, int]
]]

- self._current_match = None # type: current_match_type
+ self._current_match: current_match_type = None
self.context = 30
self.context_delta = 0
self.allow_skip_link = True
@@ -716,10 +716,10 @@
self.allow_replace_label = False
self.allow_replace_all = False
# Use list to preserve order
- self._own_choices = [
+ self._own_choices: List[Tuple[str, StandardOption]] = [
('skip_link', StaticChoice('Do not change', 'n', None)),
('unlink', StaticChoice('Unlink', 'u', False)),
- ] # type: List[Tuple[str, StandardOption]]
+ ]
if self._new:
self._own_choices += [
('replace', LinkChoice('Change link target', 't', self,
@@ -732,7 +732,7 @@
True, True)),
]

- self.additional_choices = [] # type: List[StandardOption]
+ self.additional_choices: List[StandardOption] = []

def handle_answer(self, choice: str) -> Any:
"""Return the result for replace_links."""
@@ -899,7 +899,7 @@
module_name = calledModuleName() or 'terminal-interface'
non_global_args = []
username = None
- do_help_val = None if do_help else False # type: Union[bool, str, None]
+ do_help_val: Union[bool, str, None] = None if do_help else False
assert args is not None
for arg in args:
option, _, value = arg.partition(':')
@@ -1025,7 +1025,7 @@

try:
module = import_module(module_name)
- help_text = module.__doc__ # type: str # type: ignore[assignment]
+ help_text: str = module.__doc__ # type: ignore[assignment]
if hasattr(module, 'docuReplacements'):
for key, value in module.docuReplacements.items():
help_text = help_text.replace(key, value.strip('\n\r'))
@@ -1195,7 +1195,7 @@
keyword is a :python:`dict method<library/stdtypes.html#dict.clear>`.
"""

- available_options = {} # type: Dict[str, Any]
+ available_options: Dict[str, Any] = {}
""" Handler configuration attribute.
Only the keys of the dict can be passed as `__init__` options.
The values are the default values. Overwrite this in subclasses!
@@ -1254,7 +1254,7 @@
A :attr:`counter` instance variable is provided.
"""

- use_disambigs = None # type: Optional[bool]
+ use_disambigs: Optional[bool] = None
"""Attribute to determine whether to use disambiguation pages. Set
it to True to use disambigs only, set it to False to skip disambigs.
If None both are processed.
@@ -1262,7 +1262,7 @@
.. versionadded:: 7.2
"""

- use_redirects = None # type: Optional[bool]
+ use_redirects: Optional[bool] = None
"""Attribute to determine whether to use redirect pages. Set it to
True to use redirects only, set it to False to skip redirects. If
None both are processed. For example to create a RedirectBot you may
@@ -1283,7 +1283,7 @@
'always': False, # By default ask for confirmation when putting a page
}

- update_options = {} # type: Dict[str, Any]
+ update_options: Dict[str, Any] = {}
"""`update_options` can be used to update :attr:`available_options`;
do not use it if the bot class is to be derived but use
`self.available_options.update(<dict>)` initializer in such case.
@@ -1291,7 +1291,7 @@
.. versionadded:: 6.4
"""

- _current_page = None # type: Optional[pywikibot.page.BasePage]
+ _current_page: Optional['pywikibot.page.BasePage'] = None

def __init__(self, **kwargs: Any) -> None:
"""Initializer.
@@ -1306,12 +1306,12 @@
else:
#: instance variable to hold the generator processed by
#: :meth:`run` method
- self.generator = kwargs.pop('generator') # type: Iterable
+ self.generator: Iterable = kwargs.pop('generator')

self.available_options.update(self.update_options)
super().__init__(**kwargs)

- self.counter = Counter() # type: Counter
+ self.counter: Counter = Counter()
"""Instance variable which holds counters. The default counters
are 'read', 'write' and 'skip'. You can use your own counters like::

@@ -1322,7 +1322,7 @@
Your additional counters are also printed during :meth:`exit`
"""

- self.generator_completed = False # type: bool
+ self.generator_completed: bool = False
"""Instance attribute which is True if the generator is completed.

To check for an empty generator you may use::
@@ -1337,7 +1337,7 @@
"""

#: instance variable to hold the default page type
- self.treat_page_type = pywikibot.page.BasePage # type: Any
+ self.treat_page_type: Any = pywikibot.page.BasePage

@property
@deprecated("self.counter['read']", since='7.0.0')
@@ -1801,7 +1801,7 @@
pywikibot.Site.
"""
if site is True:
- self._site = pywikibot.Site() # type: Optional[BaseSite]
+ self._site: Optional[BaseSite] = pywikibot.Site()
elif site is False:
raise ValueError("'site' must be a site, True, or None")
else:
@@ -1979,7 +1979,7 @@
"""

#: Must be defined in subclasses.
- summary_key = None # type: Optional[str]
+ summary_key: Optional[str] = None

@property
def summary_parameters(self) -> Dict[str, str]:
@@ -2407,7 +2407,7 @@

def treat_page(self) -> None:
"""Treat a page."""
- page = self.current_page # type: Optional[pywikibot.page.BasePage]
+ page: Optional[pywikibot.page.BasePage] = self.current_page
if self.use_from_page is True:
try:
item = pywikibot.ItemPage.fromPage(page)
diff --git a/pywikibot/bot_choice.py b/pywikibot/bot_choice.py
index dbeacac..1cd8638 100644
--- a/pywikibot/bot_choice.py
+++ b/pywikibot/bot_choice.py
@@ -110,7 +110,7 @@
"""

#: Place output before or after the question
- before_question = False # type: bool
+ before_question: bool = False

@property
def stop(self) -> bool:
@@ -430,7 +430,7 @@

def format(self, default: Optional[str] = None) -> str:
"""Return a formatted string showing the range."""
- value = None # type: Optional[int]
+ value: Optional[int] = None

if default is not None and self.test(default):
value = self.parse(default)
diff --git a/pywikibot/config.py b/pywikibot/config.py
index 0862233..0e4c691 100644
--- a/pywikibot/config.py
+++ b/pywikibot/config.py
@@ -141,8 +141,8 @@
# usernames['wikibooks']['*'] = 'mySingleUsername'
# You may use '*' for family name in a similar manner.
#
-usernames = collections.defaultdict(dict) # type: Dict[str, Dict[str, str]]
-disambiguation_comment = collections.defaultdict(dict) # type: _DabComDict
+usernames: Dict[str, Dict[str, str]] = collections.defaultdict(dict)
+disambiguation_comment: _DabComDict = collections.defaultdict(dict)

# User agent format.
# For the meaning and more help in customization see:
@@ -167,7 +167,7 @@
# True for enabling, False for disabling, str to hardcode a UA.
# Example: {'problematic.site.example': True,
# 'prefers.specific.ua.example': 'snakeoil/4.2'}
-fake_user_agent_exceptions = {} # type: Dict[str, Union[bool, str]]
+fake_user_agent_exceptions: Dict[str, Union[bool, str]] = {}

# The default interface for communicating with the site
# currently the only defined interface is 'APISite', so don't change this!
@@ -215,7 +215,7 @@
# 'access_key', 'access_secret')
#
# Note: the target wiki site must install OAuth extension
-authenticate = {} # type: Dict[str, Tuple[str, ...]]
+authenticate: Dict[str, Tuple[str, ...]] = {}

# By default you are asked for a password on the terminal.
# A password file may be used, e.g. password_file = '.passwd'
@@ -268,7 +268,7 @@
#
# Note that these headers will be sent with all requests,
# not just MediaWiki API calls.
-extra_headers = {} # type: Mapping[str, str]
+extra_headers: Mapping[str, str] = {}

# Set to True to override the {{bots}} exclusion protocol (at your own risk!)
ignore_bot_templates = False
@@ -517,7 +517,7 @@
# this can be used to pass variables to the UI init function
# useful for e.g.
# userinterface_init_kwargs = {'default_stream': 'stdout'}
-userinterface_init_kwargs = {} # type: Dict[str, str]
+userinterface_init_kwargs: Dict[str, str] = {}

# i18n setting for user interface language
# default is obtained from locale.getlocale
@@ -590,9 +590,9 @@
# log = []
# Per default, no logging is enabled.
# This setting can be overridden by the -log or -nolog command-line arguments.
-log = [] # type: List[str]
+log: List[str] = []
# filename defaults to modulename-bot.log
-logfilename = None # type: Optional[str]
+logfilename: Optional[str] = None
# maximal size of a logfile in kilobytes. If the size reached that limit the
# logfile will be renamed (if logfilecount is not 0) and the old file is filled
# again. logfilesize must be an integer value
@@ -610,7 +610,7 @@
log_pywiki_repo_version = False
# if True, include a lot of debugging info in logfile
# (overrides log setting above)
-debug_log = [] # type: List[str]
+debug_log: List[str] = []

# ############# EXTERNAL SCRIPT PATH SETTINGS ##############
# Set your own script path to lookup for your script files.
@@ -630,7 +630,7 @@
#
# sample:
# user_script_paths = ['scripts.myscripts']
-user_script_paths = [] # type: List[str]
+user_script_paths: List[str] = []

# ############# EXTERNAL FAMILIES SETTINGS ##############
# Set your own family path to lookup for your family files.
@@ -645,7 +645,7 @@
# samples:
# family_files['mywiki'] = 'https://de.wikipedia.org'
# user_families_paths = ['data/families']
-user_families_paths = [] # type: List[str]
+user_families_paths: List[str] = []

# ############# INTERWIKI SETTINGS ##############

@@ -723,7 +723,7 @@

# Slow down the robot such that it never makes a second page edit within
# 'put_throttle' seconds.
-put_throttle = 10 # type: Union[int, float]
+put_throttle: Union[int, float] = 10

# Sometimes you want to know when a delay is inserted. If a delay is larger
# than 'noisysleep' seconds, it is logged on the screen.
@@ -810,7 +810,7 @@
# (if cosmetic_changes_mylang_only is set)
# Please set your dictionary by adding such lines to your user config file:
# cosmetic_changes_enable['wikipedia'] = ('de', 'en', 'fr')
-cosmetic_changes_enable = {} # type: Dict[str, Tuple[str, ...]]
+cosmetic_changes_enable: Dict[str, Tuple[str, ...]] = {}

# The dictionary cosmetic_changes_disable should contain a tuple of languages
# for each site where you wish to disable cosmetic changes. You may use it with
@@ -818,7 +818,7 @@
# language. This also overrides the settings in the cosmetic_changes_enable
# dictionary. Please set your dict by adding such lines to your user config:
# cosmetic_changes_disable['wikipedia'] = ('de', 'en', 'fr')
-cosmetic_changes_disable = {} # type: Dict[str, Tuple[str, ...]]
+cosmetic_changes_disable: Dict[str, Tuple[str, ...]] = {}

# cosmetic_changes_deny_script is a list of scripts for which cosmetic changes
# are disabled. You may add additional scripts by appending script names in
@@ -840,7 +840,7 @@
#
# to replace all occurrences of 'Hoofdpagina' with 'Veurblaad' when writing to
# liwiki. Note that this does not take the origin wiki into account.
-replicate_replace = {} # type: Dict[str, Dict[str, str]]
+replicate_replace: Dict[str, Dict[str, str]] = {}

# ############# FURTHER SETTINGS ##############

@@ -850,11 +850,11 @@
# on the wiki server. Allows simulation runs of bots to be carried out without
# changing any page on the server side. Use this setting to add more actions
# into user config file for wikis with extra write actions.
-actions_to_block = [] # type: List[str]
+actions_to_block: List[str] = []

# Set simulate to True or use -simulate option to block all actions given
# above.
-simulate = False # type: Union[bool, str]
+simulate: Union[bool, str] = False

# How many pages should be put to a queue in asynchronous mode.
# If maxsize is <= 0, the queue size is infinite.
diff --git a/pywikibot/cosmetic_changes.py b/pywikibot/cosmetic_changes.py
index 04e6513..484e784 100644
--- a/pywikibot/cosmetic_changes.py
+++ b/pywikibot/cosmetic_changes.py
@@ -489,7 +489,7 @@
return '{}|{}]]'.format(
split[0], '|'.join(cache.get(x.strip(), x) for x in split[1:]))

- cache = {} # type: Dict[Union[bool, str], Any]
+ cache: Dict[Union[bool, str], Any] = {}
exceptions = ['comment', 'nowiki', 'pre', 'syntaxhighlight']
regex = re.compile(
FILE_LINK_REGEX % '|'.join(self.site.namespaces[6]),
@@ -956,7 +956,7 @@

def fixTypo(self, text: str) -> str:
"""Fix units."""
- exceptions = [
+ exceptions: List[Union[str, Pattern[str]]] = [
'comment',
'gallery',
'hyperlink',
@@ -967,7 +967,7 @@
'pre',
'startspace',
'syntaxhighlight',
- ] # type: List[Union[str, Pattern[str]]]
+ ]

# change <number> ccm -> <number> cm³
text = textlib.replaceExcept(text, r'(\d)\s*(?:&nbsp;)?ccm',
@@ -990,7 +990,7 @@
if self.site.code not in ['ckb', 'fa']:
return text

- exceptions = [
+ exceptions: List[Union[str, Pattern[str]]] = [
'file',
'gallery',
'hyperlink',
@@ -1005,7 +1005,7 @@
'ref',
'startspace',
'syntaxhighlight',
- ] # type: List[Union[str, Pattern[str]]]
+ ]

digits = textlib.NON_LATIN_DIGITS
faChrs = 'ءاآأإئؤبپتثجچحخدذرزژسشصضطظعغفقکگلمنوهیةيك' + digits['fa']
diff --git a/pywikibot/data/api/_requests.py b/pywikibot/data/api/_requests.py
index f9c37dd..bdfcd74 100644
--- a/pywikibot/data/api/_requests.py
+++ b/pywikibot/data/api/_requests.py
@@ -225,7 +225,7 @@
raise ValueError("'action' specification missing from Request.")
self.action = parameters['action']
self.update(parameters) # also convert all parameter values to lists
- self._warning_handler = None # type: Optional[Callable[[str, str], Union[Match[str], bool, None]]] # noqa: E501
+ self._warning_handler: Optional[Callable[[str, str], Union[Match[str], bool, None]]] = None # noqa: E501
self.write = self.action in WRITE_ACTIONS
# Client side verification that the request is being performed
# by a logged in user, and warn if it isn't a config username.
diff --git a/pywikibot/date.py b/pywikibot/date.py
index 21d2398..5e167f2 100644
--- a/pywikibot/date.py
+++ b/pywikibot/date.py
@@ -350,7 +350,7 @@

# Each tuple must 3 parts: a list of all possible digits (symbols), encoder
# (from int to a str) and decoder (from str to an int)
-_digitDecoders = {
+_digitDecoders: Dict[str, decoder_type] = {
# %% is a %
'%': '%',
# %d is a decimal
@@ -381,7 +381,7 @@
# %T is a year in TH: -- all years are shifted: 2005 => 'พ.ศ. 2548'
'T': (_decimalDigits, lambda v: str(v + 543),
lambda v: int(v) - 543),
-} # type: Dict[str, decoder_type]
+}

# Allows to search for '(%%)|(%d)|(%R)|...", and allows one digit 1-9 to set
# the size of zero-padding for numbers
@@ -434,7 +434,7 @@
if pattern not in _escPtrnCache2:
newPattern = '' # match starts at the beginning of the string
strPattern = ''
- decoders = [] # type: List[decoder_type]
+ decoders: List[decoder_type] = []
for s in _reParameters.split(pattern):
if s is None:
continue
@@ -663,7 +663,7 @@
"""
self.index = index
self.variant, _, self.month = format_key.partition('_')
- self.data = {} # type: Dict[str, Callable[[int], str]]
+ self.data: Dict[str, Callable[[int], str]] = {}

def __getitem__(self, key: str) -> Callable[[int], str]:
if key not in self.data:
@@ -722,7 +722,7 @@
alwaysTrue)])


-formats = {
+formats: Dict[Union[str, int], Mapping[str, Callable[[int], str]]] = {
'MonthName': MonthNames(),
'Number': {
'ar': lambda v: dh_number(v, '%d (عدد)'),
@@ -1654,7 +1654,7 @@
'yo': lambda v: dh_singVal(v, 'Current events'),
'zh': lambda v: dh_singVal(v, '新闻动态'),
},
-} # type: Dict[Union[str, int], Mapping[str, Callable[[int], str]]]
+}

#
# Add auto-generated empty dictionaries for DayOfMonth and MonthOfYear articles
diff --git a/pywikibot/diff.py b/pywikibot/diff.py
index c8f397f..29ea3d5 100644
--- a/pywikibot/diff.py
+++ b/pywikibot/diff.py
@@ -114,7 +114,7 @@
"""Color diff lines."""
diff = iter(self.diff)

- fmt = '' # type: Optional[str]
+ fmt: Optional[str] = ''
line1, line2 = '', next(diff)
for line in diff:
fmt, line1, line2 = line1, line2, line
@@ -263,8 +263,8 @@
:param replace_invisible: Replace invisible characters like U+200e with
the charnumber in brackets (e.g. <200e>).
"""
- self.a = text_a.splitlines(True) # type: Union[str, List[str]]
- self.b = text_b.splitlines(True) # type: Union[str, List[str]]
+ self.a: Union[str, List[str]] = text_a.splitlines(True)
+ self.b: Union[str, List[str]] = text_b.splitlines(True)
if by_letter and len(self.a) <= 1 and len(self.b) <= 1:
self.a = text_a
self.b = text_b
@@ -338,7 +338,7 @@

if self.context:
# Determine if two hunks are connected by self.context
- super_hunk = [] # type: List[Hunk]
+ super_hunk: List[Hunk] = []
super_hunks = [super_hunk]
for hunk in hunks:
# self.context * 2, because if self.context is 2 the hunks
@@ -417,7 +417,7 @@

super_hunks = self._generate_super_hunks(
h for h in self.hunks if h.reviewed == Hunk.PENDING)
- position = 0 # type: Optional[int]
+ position: Optional[int] = 0

while any(any(hunk.reviewed == Hunk.PENDING for hunk in super_hunk)
for super_hunk in super_hunks):
@@ -540,7 +540,7 @@
'Please review them before proceeding.\n')
self.review_hunks()

- l_text = [] # type: List[str]
+ l_text: List[str] = []
for hunk_idx, (i1, i2), (j1, j2) in self.blocks:
# unchanged text.
if hunk_idx < 0:
@@ -604,8 +604,8 @@
"""
from bs4 import BeautifulSoup

- comparands = {'deleted-context': [],
- 'added-context': []} # type: Dict[str, List[str]]
+ comparands: Dict[str, List[str]] = {'deleted-context': [],
+ 'added-context': []}
soup = BeautifulSoup(compare_string, 'html.parser')
for change_type, css_class in (('deleted-context', 'diff-deletedline'),
('added-context', 'diff-addedline')):
diff --git a/pywikibot/echo.py b/pywikibot/echo.py
index 8b0c7b1..18112da 100644
--- a/pywikibot/echo.py
+++ b/pywikibot/echo.py
@@ -21,13 +21,13 @@
"""Initialize an empty Notification object."""
self.site = site

- self.event_id = None # type: Optional[int]
+ self.event_id: Optional[int] = None
self.type = None
self.category = None
self.timestamp = None
self.page = None
self.agent = None
- self.read = None # type: Optional[bool]
+ self.read: Optional[bool] = None
self.content = None
self.revid = None

diff --git a/pywikibot/family.py b/pywikibot/family.py
index de40fbe..dff749a 100644
--- a/pywikibot/family.py
+++ b/pywikibot/family.py
@@ -72,7 +72,7 @@

name = None

- langs = {} # type: Dict[str, str]
+ langs: Dict[str, str] = {}

# For interwiki sorting order see
# https://meta.wikimedia.org/wiki/Interwiki_sorting_order
@@ -173,12 +173,12 @@

# A dict of tuples for different sites with names of templates
# that indicate an edit should be avoided
- edit_restricted_templates = {} # type: Dict[str, Tuple[str, ...]]
+ edit_restricted_templates: Dict[str, Tuple[str, ...]] = {}

# A dict of tuples for different sites with names of archive
# templates that indicate an edit of non-archive bots
# should be avoided
- archived_page_templates = {} # type: Dict[str, Tuple[str, ...]]
+ archived_page_templates: Dict[str, Tuple[str, ...]] = {}

# A list of projects that share cross-project sessions.
cross_projects = []
@@ -191,34 +191,34 @@
cross_projects_cookie_username = 'centralauth_User'

# A list with the name in the cross-language flag permissions
- cross_allowed = [] # type: List[str]
+ cross_allowed: List[str] = []

# A dict with the name of the category containing disambiguation
# pages for the various languages. Only one category per language,
# and without the namespace, so add things like:
# 'en': "Disambiguation"
- disambcatname = {} # type: Dict[str, str]
+ disambcatname: Dict[str, str] = {}

# attop is a list of languages that prefer to have the interwiki
# links at the top of the page.
- interwiki_attop = [] # type: List[str]
+ interwiki_attop: List[str] = []
# on_one_line is a list of languages that want the interwiki links
# one-after-another on a single line
- interwiki_on_one_line = [] # type: List[str]
+ interwiki_on_one_line: List[str] = []
# String used as separator between interwiki links and the text
interwiki_text_separator = '\n\n'

# Similar for category
- category_attop = [] # type: List[str]
+ category_attop: List[str] = []
# on_one_line is a list of languages that want the category links
# one-after-another on a single line
- category_on_one_line = [] # type: List[str]
+ category_on_one_line: List[str] = []
# String used as separator between category links and the text
category_text_separator = '\n\n'
# When both at the bottom should categories come after interwikilinks?
# TODO: T86284 Needed on Wikia sites, as it uses the CategorySelect
# extension which puts categories last on all sites. TO BE DEPRECATED!
- categories_last = [] # type: List[str]
+ categories_last: List[str] = []

# Which languages have a special order for putting interlanguage
# links, and what order is it? If a language is not in
@@ -238,15 +238,15 @@
# 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.
- interwiki_replacements = {} # type: Dict[str, str]
+ interwiki_replacements: Dict[str, str] = {}

# Codes that should be removed, usually because the site has been
# taken down.
- interwiki_removals = [] # type: List[str]
+ interwiki_removals: List[str] = []

# Language codes of the largest wikis. They should be roughly sorted
# by size.
- languages_by_size = [] # type: List[str]
+ languages_by_size: List[str] = []

# Some languages belong to a group where the possibility is high that
# equivalent articles have identical titles among the group.
@@ -339,7 +339,7 @@

# Some wiki farms have UrlShortener extension enabled only on the main
# site. This value can specify this last one with (lang, family) tuple.
- shared_urlshortner_wiki = None # type: Optional[Tuple[str, str]]
+ shared_urlshortner_wiki: Optional[Tuple[str, str]] = None

title_delimiter_and_aliases = ' _'
"""Titles usually are delimited by a space and the alias is replaced
@@ -955,9 +955,9 @@
}

# Not open for edits; stewards can still edit.
- closed_wikis = [] # type: List[str]
+ closed_wikis: List[str] = []
# Completely removed
- removed_wikis = [] # type: List[str]
+ removed_wikis: List[str] = []

# WikimediaFamily uses Wikibase for the category name containing
# disambiguation pages for the various languages. We need the
diff --git a/pywikibot/flow.py b/pywikibot/flow.py
index 3f58475..4e2984e 100644
--- a/pywikibot/flow.py
+++ b/pywikibot/flow.py
@@ -100,7 +100,7 @@
raise ValueError('Illegal board data (missing required data).')
parsed_url = urlparse(rule['url'])
params = parse_qs(parsed_url.query)
- new_params = {} # type: Dict[str, Any]
+ new_params: Dict[str, Any] = {}
for key, value in params.items():
if key != 'title':
key = key.replace('topiclist_', '').replace('-', '_')
@@ -326,7 +326,7 @@
self._page = page
self._uuid = uuid

- self._content = {} # type: Dict[str, Any]
+ self._content: Dict[str, Any] = {}

@classmethod
def fromJSON(cls, page: 'Topic', post_uuid: str, # noqa: N802
diff --git a/pywikibot/i18n.py b/pywikibot/i18n.py
index 418a029..079b67f 100644
--- a/pywikibot/i18n.py
+++ b/pywikibot/i18n.py
@@ -275,7 +275,7 @@
'zh-tw': 'zh-classical',
'zh-yue': 'cdo'})

-_GROUP_NAME_TO_FALLBACKS = {
+_GROUP_NAME_TO_FALLBACKS: Dict[str, List[str]] = {
'': [],
'aa': ['am'],
'ab': ['ru'],
@@ -358,7 +358,7 @@
'zh-classical': ['zh', 'zh-hans', 'zh-tw', 'zh-cn', 'zh-classical', 'lzh'],
'zh-min-nan': [
'cdo', 'zh', 'zh-hans', 'zh-tw', 'zh-cn', 'zh-classical', 'lzh']
-} # type: Dict[str, List[str]]
+}


def set_messages_package(package_name: str) -> None:
diff --git a/pywikibot/interwiki_graph.py b/pywikibot/interwiki_graph.py
index 209d4f2..d5bc153 100644
--- a/pywikibot/interwiki_graph.py
+++ b/pywikibot/interwiki_graph.py
@@ -71,7 +71,7 @@
# pages are values. It stores where we found each page.
# As we haven't yet found a page that links to the origin page, we
# start with an empty list for it.
- self.found_in = {} # type: FoundInType
+ self.found_in: FoundInType = {}
if origin:
self.found_in = {origin: []}

@@ -99,7 +99,7 @@
if PYDOT_ERROR:
msg = f'pydot is not installed: {PYDOT_ERROR}.'
raise ImportError(msg)
- self.graph = None # type: Optional[pydot.Dot]
+ self.graph: Optional[pydot.Dot] = None
self.subject = subject

@staticmethod
diff --git a/pywikibot/logentries.py b/pywikibot/logentries.py
index b295cbc..2eb9ff2 100644
--- a/pywikibot/logentries.py
+++ b/pywikibot/logentries.py
@@ -30,7 +30,7 @@
# Log type expected. None for every type, or one of the (letype) str :
# block/patrol/etc...
# Overridden in subclasses.
- _expected_type = None # type: Optional[str]
+ _expected_type: Optional[str] = None

def __init__(self, apidata: Dict[str, Any],
site: 'pywikibot.site.BaseSite') -> None:
@@ -390,7 +390,7 @@
or use the get_valid_entry_class instance method instead.
"""
if logtype not in cls._logtypes:
- bases = (OtherLogEntry, ) # type: Tuple['LogEntry', ...]
+ bases: Tuple['LogEntry', ...] = (OtherLogEntry, )
if logtype in ('newusers', 'thanks'):
bases = (UserTargetLogEntry, OtherLogEntry)

diff --git a/pywikibot/logging.py b/pywikibot/logging.py
index 0955418..9dafa11 100644
--- a/pywikibot/logging.py
+++ b/pywikibot/logging.py
@@ -46,7 +46,7 @@
.. seealso:: :python:`Python Logging Levels<logging.html#logging-levels>`
"""

-_init_routines = [] # type: List[Callable[[], Any]]
+_init_routines: List[Callable[[], Any]] = []
_inited_routines = set()


diff --git a/pywikibot/login.py b/pywikibot/login.py
index 0581b8b..7fc55de 100644
--- a/pywikibot/login.py
+++ b/pywikibot/login.py
@@ -377,7 +377,7 @@
'should be removed if OAuth enabled.'
.format(login=self))
self._consumer_token = (user, password)
- self._access_token = None # type: Optional[Tuple[str, str]]
+ self._access_token: Optional[Tuple[str, str]] = None

def login(self, retry: bool = False, force: bool = False) -> bool:
"""
diff --git a/pywikibot/page/_wikibase.py b/pywikibot/page/_wikibase.py
index 1e647a9..77ac917 100644
--- a/pywikibot/page/_wikibase.py
+++ b/pywikibot/page/_wikibase.py
@@ -89,7 +89,7 @@
:type title_pattern: str
"""

- DATA_ATTRIBUTES = {} # type: Dict[str, Any]
+ DATA_ATTRIBUTES: Dict[str, Any] = {}

def __init__(self, repo, id_=None) -> None:
"""
diff --git a/pywikibot/pagegenerators/__init__.py b/pywikibot/pagegenerators/__init__.py
index 356f24b..ef08476 100644
--- a/pywikibot/pagegenerators/__init__.py
+++ b/pywikibot/pagegenerators/__init__.py
@@ -591,7 +591,7 @@
kwargs.pop('start', None) # don't set start time
kwargs.pop('end', None) # don't set stop time

- seen = set() # type: Set[Any]
+ seen: Set[Any] = set()
while total is None or len(seen) < total:
def filtered_generator() -> Iterable['pywikibot.page.Page']:
for item in generator(total=None if seen else 1, **kwargs):
@@ -619,7 +619,7 @@
"""
# pages may be on more than one site, for example if an interwiki
# generator is used, so use a separate preloader for each site
- sites = {} # type: PRELOAD_SITE_TYPE
+ sites: PRELOAD_SITE_TYPE = {}
# build a list of pages for each site found in the iterator
for page in generator:
site = page.site
@@ -662,7 +662,7 @@
:param generator: pages to iterate over
:param groupsize: how many pages to preload at once
"""
- sites = {} # type: PRELOAD_SITE_TYPE
+ sites: PRELOAD_SITE_TYPE = {}
for page in generator:
site = page.site
sites.setdefault(site, []).append(page)
diff --git a/pywikibot/pagegenerators/_factory.py b/pywikibot/pagegenerators/_factory.py
index a67280a..41d345f 100644
--- a/pywikibot/pagegenerators/_factory.py
+++ b/pywikibot/pagegenerators/_factory.py
@@ -96,25 +96,25 @@
:param disabled_options: disable these given options and let them
be handled by scripts options handler
"""
- self.gens = [] # type: List[Iterable['pywikibot.page.Page']]
- self._namespaces = [] # type: GEN_FACTORY_NAMESPACE_TYPE
- self.limit = None # type: Optional[int]
- self.qualityfilter_list = [] # type: List[int]
- self.articlefilter_list = [] # type: List[str]
- self.articlenotfilter_list = [] # type: List[str]
- self.titlefilter_list = [] # type: List[str]
- self.titlenotfilter_list = [] # type: List[str]
- self.claimfilter_list = [] # type: GEN_FACTORY_CLAIM_TYPE
- self.catfilter_list = [] # type: List['pywikibot.Category']
+ self.gens: List[Iterable['pywikibot.page.Page']] = []
+ self._namespaces: GEN_FACTORY_NAMESPACE_TYPE = []
+ self.limit: Optional[int] = None
+ self.qualityfilter_list: List[int] = []
+ self.articlefilter_list: List[str] = []
+ self.articlenotfilter_list: List[str] = []
+ self.titlefilter_list: List[str] = []
+ self.titlenotfilter_list: List[str] = []
+ self.claimfilter_list: GEN_FACTORY_CLAIM_TYPE = []
+ self.catfilter_list: List['pywikibot.Category'] = []
self.intersect = False
- self.subpage_max_depth = None # type: Optional[int]
+ self.subpage_max_depth: Optional[int] = None
self._site = site
self._positional_arg_name = positional_arg_name
- self._sparql = None # type: Optional[str]
+ self._sparql: Optional[str] = None
self.nopreload = False
self._validate_options(enabled_options, disabled_options)

- self.is_preloading = None # type: Optional[bool]
+ self.is_preloading: Optional[bool] = None
"""Return whether Page objects are preloaded. You may use this
instance variable after :meth:`getCombinedGenerator` is called
e.g.::
@@ -307,7 +307,7 @@
category = i18n.input('pywikibot-enter-category-name')
category = category.replace('#', '|')

- startfrom = None # type: Optional[str]
+ startfrom: Optional[str] = None
category, _, startfrom = category.partition('|')

if not startfrom:
@@ -432,7 +432,7 @@
valid_cats = [c for _list in cats.values() for c in _list]

value = value or ''
- lint_from = None # type: Optional[str]
+ lint_from: Optional[str] = None
cat, _, lint_from = value.partition('/')
lint_from = lint_from or None

@@ -923,7 +923,7 @@
:param arg: Pywikibot argument consisting of -name:value
:return: True if the argument supplied was recognised by the factory
"""
- value = None # type: Optional[str]
+ value: Optional[str] = None

if not arg.startswith('-') and self._positional_arg_name:
value = arg
diff --git a/pywikibot/pagegenerators/_generators.py b/pywikibot/pagegenerators/_generators.py
index 78996d2..3b60393 100644
--- a/pywikibot/pagegenerators/_generators.py
+++ b/pywikibot/pagegenerators/_generators.py
@@ -65,7 +65,7 @@
if site is None:
site = pywikibot.Site()

- filterredir = None # type: Optional[bool]
+ filterredir: Optional[bool] = None
if not includeredirects:
filterredir = False
elif includeredirects == 'only':
@@ -104,7 +104,7 @@
namespace = prefixlink.namespace
title = prefixlink.title

- filterredir = None # type: Optional[bool]
+ filterredir: Optional[bool] = None
if not includeredirects:
filterredir = False
elif includeredirects == 'only':
@@ -930,7 +930,7 @@
self.content = content
self.skipping = bool(start)

- self.start = None # type: Optional[str]
+ self.start: Optional[str] = None
if start is not None and self.skipping:
self.start = start.replace('_', ' ')

diff --git a/pywikibot/plural.py b/pywikibot/plural.py
index 45b015d..d5f6e94 100644
--- a/pywikibot/plural.py
+++ b/pywikibot/plural.py
@@ -11,7 +11,7 @@

PluralRule = Dict[str, Union[int, Callable[[int], Union[bool, int]]]]

-plural_rules = {
+plural_rules: Dict[str, PluralRule] = {
'_default': {'nplurals': 2, 'plural': lambda n: (n != 1)},
'ar': {'nplurals': 6, 'plural': lambda n:
0 if (n == 0) else
@@ -83,7 +83,7 @@
1 if (n % 100 == 2) else
2 if n % 100 in (3, 4) else
3},
-} # type: Dict[str, PluralRule]
+}

plural_rules.update(
dict.fromkeys(
diff --git a/pywikibot/proofreadpage.py b/pywikibot/proofreadpage.py
index 8c8432a..350f11a 100644
--- a/pywikibot/proofreadpage.py
+++ b/pywikibot/proofreadpage.py
@@ -243,7 +243,7 @@
:return: (base, ext, num).
"""
left, sep, right = self.title(with_ns=False).rpartition('/')
- num = None # type: Optional[int]
+ num: Optional[int] = None

if sep:
base = left
@@ -279,7 +279,7 @@
set(self.getReferences(namespaces=index_ns))]

if not what_links_here:
- self._index = (None, []) # type: _IndexType
+ self._index: _IndexType = (None, [])
elif len(what_links_here) == 1:
self._index = (what_links_here.pop(), [])
else:
@@ -927,11 +927,11 @@
"""Associate label and number for each page linked to the index."""
# Clean cache, if any.
self._page_from_numbers = {}
- self._numbers_from_page = {} # type: Dict[pywikibot.page.Page, int]
- self._page_numbers_from_label = {} # type: Dict[str, Set[int]]
- self._pages_from_label = {} # type: PagesFromLabelType
- self._labels_from_page_number = {} # type: Dict[int, str]
- self._labels_from_page = {} # type: Dict[pywikibot.page.Page, str]
+ self._numbers_from_page: Dict[pywikibot.page.Page, int] = {}
+ self._page_numbers_from_label: Dict[str, Set[int]] = {}
+ self._pages_from_label: PagesFromLabelType = {}
+ self._labels_from_page_number: Dict[int, str] = {}
+ self._labels_from_page: Dict[pywikibot.page.Page, str] = {}
self._soup = _bs4_soup(self.get_parsed_page(True)) # type: ignore
# Do not search for "new" here, to avoid to skip purging if links
# to non-existing pages are present.
diff --git a/pywikibot/scripts/generate_user_files.py b/pywikibot/scripts/generate_user_files.py
index d88e229..649a6e7 100755
--- a/pywikibot/scripts/generate_user_files.py
+++ b/pywikibot/scripts/generate_user_files.py
@@ -176,6 +176,9 @@
# This is an automatically generated file. You can find more
# configuration parameters in 'config.py' file or refer
# https://doc.wikimedia.org/pywikibot/master/api_ref/pywikibot.config.html
+from typing import Optional, Union
+
+from pywikibot.backports import Dict, List, Tuple

# The family of sites to be working on.
# Pywikibot will import families/xxx_family.py so if you want to change
diff --git a/pywikibot/site/_apisite.py b/pywikibot/site/_apisite.py
index d316b81..7e53bbf 100644
--- a/pywikibot/site/_apisite.py
+++ b/pywikibot/site/_apisite.py
@@ -78,7 +78,7 @@


__all__ = ('APISite', )
-_mw_msg_cache = defaultdict(dict) # type: DefaultDict[str, Dict[str, str]]
+_mw_msg_cache: DefaultDict[str, Dict[str, str]] = defaultdict(dict)


_CompType = Union[int, str, 'pywikibot.page.Page', 'pywikibot.page.Revision']
@@ -115,10 +115,10 @@
) -> None:
"""Initializer."""
super().__init__(code, fam, user)
- self._globaluserinfo = {} # type: Dict[Union[int, str], Any]
+ self._globaluserinfo: Dict[Union[int, str], Any] = {}
self._interwikimap = _InterwikiMap(self)
self._loginstatus = _LoginStatus.NOT_ATTEMPTED
- self._msgcache = {} # type: Dict[str, str]
+ self._msgcache: Dict[str, str] = {}
self._paraminfo = api.ParamInfo(self)
self._siteinfo = Siteinfo(self)
self.tokens = TokenWallet(self)
@@ -244,7 +244,7 @@
:raises TypeError: a namespace identifier has an inappropriate
type such as NoneType or bool
"""
- req_args = {'site': self} # type: Dict[str, Any]
+ req_args: Dict[str, Any] = {'site': self}
if 'g_content' in args:
req_args['g_content'] = args.pop('g_content')
if 'parameters' in args:
@@ -535,7 +535,7 @@

:raises TypeError: Inappropriate argument type of 'user'
"""
- param = {} # type: Dict[str, Union[int, str]]
+ param: Dict[str, Union[int, str]] = {}
if user is None:
user = self.username()
assert isinstance(user, str)
@@ -642,7 +642,7 @@
"API userinfo response lacks 'query' key"
assert 'userinfo' in uidata['query'], \
"API userinfo response lacks 'userinfo' key"
- self._useroptions = uidata['query']['userinfo']['options'] # type: Dict[str, Any] # noqa: E501
+ self._useroptions: Dict[str, Any] = uidata['query']['userinfo']['options'] # noqa: E501
# To determine if user name has changed
self._useroptions['_name'] = (
None if 'anon' in uidata['query']['userinfo'] else
@@ -881,7 +881,7 @@

months = self.mediawiki_messages(months_long + months_short)

- self._months_names = [] # type: List[Tuple[str, str]]
+ self._months_names: List[Tuple[str, str]] = []
for m_l, m_s in zip(months_long, months_short):
self._months_names.append((months[m_l], months[m_s]))

@@ -1700,7 +1700,7 @@

# Catalog of editpage error codes, for use in generating messages.
# The block at the bottom are page related errors.
- _ep_errors = {
+ _ep_errors: Dict[str, Union[str, Type[PageSaveRelatedError]]] = {
'noapiwrite': 'API editing not enabled on {site} wiki',
'writeapidenied':
'User {user} is not authorized to edit on {site} wiki',
@@ -1731,7 +1731,7 @@
'titleblacklist-forbidden': TitleblacklistError,
'spamblacklist': SpamblacklistError,
'abusefilter-disallowed': AbuseFilterDisallowedError,
- } # type: Dict[str, Union[str, Type[PageSaveRelatedError]]]
+ }
_ep_text_overrides = {'appendtext', 'prependtext', 'undo'}

@need_right('edit')
@@ -2052,7 +2052,7 @@
raise Error('mergehistory: unexpected response')

# catalog of move errors for use in error messages
- _mv_errors = {
+ _mv_errors: Dict[str, Union[str, OnErrorExc]] = {
'noapiwrite': 'API editing not enabled on {site} wiki',
'writeapidenied':
'User {user} is not authorized to edit on {site} wiki',
@@ -2081,7 +2081,7 @@
'[[{newtitle}]] file extension does not match content of '
'[[{oldtitle}]]',
'missingtitle': "{oldtitle} doesn't exist",
- } # type: Dict[str, Union[str, OnErrorExc]]
+ }

@need_right('move')
def movepage(
diff --git a/scripts/add_text.py b/scripts/add_text.py
index 77aaa5a..2b270e9 100755
--- a/scripts/add_text.py
+++ b/scripts/add_text.py
@@ -71,7 +71,7 @@


ARGS_TYPE = Dict[str, Union[bool, str]]
-DEFAULT_ARGS = {
+DEFAULT_ARGS: ARGS_TYPE = {
'text': '',
'textfile': '',
'summary': '',
@@ -83,7 +83,7 @@
'talk_page': False,
'reorder': True,
'regex_skip_url': '',
-} # type: ARGS_TYPE
+}

ARG_PROMPT = {
'-text': 'What text do you want to add?',
diff --git a/scripts/blockpageschecker.py b/scripts/blockpageschecker.py
index 36be5d7..6feda12 100755
--- a/scripts/blockpageschecker.py
+++ b/scripts/blockpageschecker.py
@@ -300,7 +300,7 @@
# keep track of the changes for each step (edit then move)
changes = -1

- msg_type = None # type: Optional[str]
+ msg_type: Optional[str] = None
edit_restriction = restrictions.get('edit')
if not edit_restriction:
# page is not edit-protected
diff --git a/scripts/category.py b/scripts/category.py
index ea2f008..3a23f8b 100755
--- a/scripts/category.py
+++ b/scripts/category.py
@@ -279,7 +279,7 @@
self.includeonly = []
return page

- tmpl = [] # type: Sequence
+ tmpl: Sequence = []
with suppress(KeyError):
tmpl, _loc = moved_links[page.site.code]

@@ -620,7 +620,7 @@
self.oldtalk = self.oldcat.toggleTalkPage()

if newcat:
- self.newcat = self._makecat(newcat) # type: Optional[pywikibot.Category] # noqa: E501
+ self.newcat: Optional[pywikibot.Category] = self._makecat(newcat) # noqa: E501
self.newtalk = self.newcat.toggleTalkPage()
else:
self.newcat = None
diff --git a/scripts/redirect.py b/scripts/redirect.py
index 402c719..27123c0 100755
--- a/scripts/redirect.py
+++ b/scripts/redirect.py
@@ -683,8 +683,8 @@

:param args: command line arguments
"""
- options = {} # type: Dict[str, Any]
- gen_options = {} # type: Dict[str, Any]
+ options: Dict[str, Any] = {}
+ gen_options: Dict[str, Any] = {}
# what the bot should do (either resolve double redirs, or process broken
# redirs)
action = None
diff --git a/scripts/replace.py b/scripts/replace.py
index fd25330..16d938a 100755
--- a/scripts/replace.py
+++ b/scripts/replace.py
@@ -917,7 +917,7 @@
# if -xml flag is present
xmlFilename = None
xmlStart = None
- sql_query = None # type: Optional[str]
+ sql_query: Optional[str] = None
# Set the default regular expression flags
flags = 0
# Request manual replacements even if replacements are already defined
diff --git a/scripts/weblinkchecker.py b/scripts/weblinkchecker.py
index 8f41b83..c6ed186 100755
--- a/scripts/weblinkchecker.py
+++ b/scripts/weblinkchecker.py
@@ -249,7 +249,7 @@
"""

#: Collecting start time of a thread for any host
- hosts = {} # type: Dict[str, float]
+ hosts: Dict[str, float] = {}
lock = threading.Lock()

def __init__(self, page, url, history, http_ignores, day) -> None:
diff --git a/scripts/welcome.py b/scripts/welcome.py
index 1335a43..36f45f2 100755
--- a/scripts/welcome.py
+++ b/scripts/welcome.py
@@ -467,9 +467,9 @@
"""Initializer."""
super().__init__(**kwargs)
self.check_managed_sites()
- self.bname = {} # type: Dict[str, str]
+ self.bname: Dict[str, str] = {}

- self.welcomed_users = [] # type: List[str]
+ self.welcomed_users: List[str] = []
self.log_name = i18n.translate(self.site, logbook)

if not self.log_name:
diff --git a/tests/__init__.py b/tests/__init__.py
index 1966c56..87b0452 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -170,7 +170,7 @@
}

# remove "# pragma: no cover" below if this set is not empty
-disabled_tests = {} # type: Dict[str, List[str]]
+disabled_tests: Dict[str, List[str]] = {}


def _unknown_test_modules():

To view, visit change 835299. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: Icf47dbe653f015c4819251501e1c0a11439181c6
Gerrit-Change-Number: 835299
Gerrit-PatchSet: 6
Gerrit-Owner: JJMC89 <JJMC89.Wikimedia@gmail.com>
Gerrit-Reviewer: JJMC89 <JJMC89.Wikimedia@gmail.com>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged