jenkins-bot has submitted this change. (
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1189971?usp=email )
Change subject: MyPy: Solve some MyPy issues in several files
......................................................................
MyPy: Solve some MyPy issues in several files
Also fix pre-commit mypy check
Change-Id: I1f9000f9b33df83dda68b6c7734dfc0d5805442c
---
M .pre-commit-config.yaml
M conftest.py
M pywikibot/backports.py
M pywikibot/comms/eventstreams.py
M pywikibot/data/api/__init__.py
M pywikibot/data/api/_optionset.py
M pywikibot/data/api/_paraminfo.py
M pywikibot/data/api/_requests.py
M pywikibot/data/sparql.py
M pywikibot/diff.py
M pywikibot/page/_category.py
M pywikibot/page/_collections.py
M pywikibot/page/_user.py
M pywikibot/pagegenerators/_factory.py
M pywikibot/pagegenerators/_filters.py
M pywikibot/site/_extensions.py
M tox.ini
17 files changed, 113 insertions(+), 76 deletions(-)
Approvals:
Xqt: Looks good to me, approved
jenkins-bot: Verified
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4544265..cc0cae2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -119,16 +119,22 @@
args:
- --config-file=pyproject.toml
- --follow-imports=silent
+ additional_dependencies:
+ - types-PyMySQL
+ - types-requests
# Test for files which already passed in past.
# They should be also used in conftest.py to exclude them from
non-voting mypy test.
- files: >
-
^pywikibot/(__metadata__|config|echo|exceptions|fixes|logging|time)\.py$|
- ^pywikibot/(comms|data|families|specialbots)/__init__\.py$|
- ^pywikibot/data/memento\.py$|
- ^pywikibot/families/[a-z][a-z\d]+_family\.py$|
- ^pywikibot/page/(__init__|_decorators|_revision)\.py$|
- ^pywikibot/pagegenerators/__init__\.py$|
- ^pywikibot/scripts/(?:i18n/)?__init__\.py$|
-
^pywikibot/site/(__init__|_basesite|_decorators|_extensions|_interwikimap|_tokenwallet|_upload)\.py$|
- ^pywikibot/tools/(_logging|_unidata|formatter)\.py$|
-
^pywikibot/userinterfaces/(__init__|_interface_base|terminal_interface)\.py$
+ files: |
+ (?x)^pywikibot/(
+
(__metadata__|backports|config|diff|echo|exceptions|fixes|logging|time)|
+ (comms|data|families|specialbots)/__init__|
+ comms/eventstreams|
+ data/(api/(__init__|_optionset)|memento)|
+ families/[a-z][a-z\d]+_family|
+ page/(__init__|_decorators|_revision)|
+ pagegenerators/(__init__|_filters)|
+ scripts/(?:i18n/)?__init__|
+
site/(__init__|_basesite|_decorators|_interwikimap|_tokenwallet|_upload)|
+ tools/(_logging|_unidata|formatter)|
+ userinterfaces/(__init__|_interface_base|terminal_interface)
+ )\.py$
diff --git a/conftest.py b/conftest.py
index c009871..5e92cd4 100644
--- a/conftest.py
+++ b/conftest.py
@@ -16,14 +16,15 @@
EXCLUDE_PATTERN = re.compile(
r'(?:'
- r'(__metadata__|config|echo|exceptions|fixes|logging|time)|'
+ r'(__metadata__|backports|config|diff|echo|exceptions|fixes|logging|time)|'
r'(comms|data|families|specialbots)/__init__|'
- r'data/memento|'
+ r'comms/eventstreams|'
+ r'data/(api/(__init__|_optionset)|memento)|'
r'families/[a-z][a-z\d]+_family|'
r'page/(__init__|_decorators|_revision)|'
- r'pagegenerators/__init__|'
+ r'pagegenerators/(__init__|_filters)|'
r'scripts/(i18n/)?__init__|'
- r'site/(__init__|_basesite|_decorators|_extensions|_interwikimap|'
+ r'site/(__init__|_basesite|_decorators|_interwikimap|'
r'_tokenwallet|_upload)|'
r'tools/(_logging|_unidata|formatter)|'
r'userinterfaces/(__init__|_interface_base|terminal_interface)'
diff --git a/pywikibot/backports.py b/pywikibot/backports.py
index 25b174b..00636aa 100644
--- a/pywikibot/backports.py
+++ b/pywikibot/backports.py
@@ -16,7 +16,7 @@
import re
import sys
-from typing import Any
+from typing import TYPE_CHECKING, Any
# Placed here to omit circular import in tools
@@ -58,6 +58,7 @@
Match,
Pattern,
Sequence,
+ Set,
)
else:
from collections import Counter
@@ -72,6 +73,7 @@
from re import Match, Pattern
Dict = dict # type: ignore[misc]
List = list # type: ignore[misc]
+ Set = set # type: ignore[misc]
if PYTHON_VERSION < (3, 9, 2):
@@ -137,8 +139,9 @@
a, b = tee(iterable)
next(b, None)
return zip(a, b)
-else:
- from itertools import pairwise # type: ignore[no-redef]
+
+elif not TYPE_CHECKING:
+ from itertools import pairwise
from types import NoneType
@@ -202,13 +205,18 @@
raise ValueError(msg)
yield tuple(group)
else: # PYTHON_VERSION == (3, 12)
- from itertools import batched as _batched
+ if TYPE_CHECKING:
+ _batched: Callable[[Iterable, int], Iterable]
+ else:
+ from itertools import batched as _batched
+
for group in _batched(iterable, n):
if strict and len(group) < n:
raise ValueError(msg)
yield group
-else:
- from itertools import batched # type: ignore[no-redef]
+
+elif not TYPE_CHECKING:
+ from itertools import batched
# gh-115942, gh-134323
@@ -291,4 +299,4 @@
return status == 'locked'
else:
- from threading import RLock
+ from threading import RLock # type: ignore[assignment]
diff --git a/pywikibot/comms/eventstreams.py b/pywikibot/comms/eventstreams.py
index 81a32fe..6db46d9 100644
--- a/pywikibot/comms/eventstreams.py
+++ b/pywikibot/comms/eventstreams.py
@@ -26,7 +26,7 @@
from requests.packages.urllib3.util.response import httplib
from pywikibot import Site, Timestamp, config, debug, warning
-from pywikibot.backports import NoneType
+from pywikibot.backports import Dict, List, NoneType
from pywikibot.comms.http import user_agent
from pywikibot.tools import cached, deprecated_args
from pywikibot.tools.collections import GeneratorWrapper
@@ -179,7 +179,7 @@
if isinstance(EventSource, ModuleNotFoundError):
raise ImportError(INSTALL_MSG) from EventSource
- self.filter = {'all': [], 'any': [], 'none': []}
+ self.filter: Dict[str, List[Any]] = {'all': [], 'any': [], 'none': []}
self._total: int | None = None
self._canary = kwargs.pop('canary', False)
diff --git a/pywikibot/data/api/__init__.py b/pywikibot/data/api/__init__.py
index 0af2e3e..c5af36c 100644
--- a/pywikibot/data/api/__init__.py
+++ b/pywikibot/data/api/__init__.py
@@ -1,6 +1,6 @@
"""Interface to MediaWiki's api.php."""
#
-# (C) Pywikibot team, 2014-2024
+# (C) Pywikibot team, 2014-2025
#
# Distributed under the terms of the MIT license.
#
@@ -55,7 +55,7 @@
"""
if isinstance(family, SubdomainFamily):
for cookie in http.cookie_jar:
- if family.domain == cookie.domain:
+ if family.domain == cookie.domain: # type: ignore[attr-defined]
http.cookie_jar.clear(cookie.domain, cookie.path, cookie.name)
@@ -71,9 +71,10 @@
def _handle_text(self, msg) -> None:
if msg['content-transfer-encoding'] == 'binary':
- self._fp.write(msg.get_payload(decode=True))
+ self._fp.write( # type: ignore[attr-defined]
+ msg.get_payload(decode=True))
else:
- super()._handle_text(msg)
+ super()._handle_text(msg) # type: ignore[misc]
_writeBody = _handle_text # noqa: N815
diff --git a/pywikibot/data/api/_optionset.py b/pywikibot/data/api/_optionset.py
index 4ba5674..c494ed4 100644
--- a/pywikibot/data/api/_optionset.py
+++ b/pywikibot/data/api/_optionset.py
@@ -9,6 +9,7 @@
from collections.abc import MutableMapping
import pywikibot
+from pywikibot.backports import Set
from pywikibot.tools import deprecate_arg
@@ -41,21 +42,26 @@
*dict* parameter was renamed to *data*.
:param site: The associated site
- :param module: The module name which is used by paraminfo. (Ignored
- when site is None)
- :param param: The parameter name inside the module. That parameter must
- have a 'type' entry. (Ignored when site is None)
+ :param module: The module name which is used by paraminfo.
+ (Ignored when site is None)
+ :param param: The parameter name inside the module. That
+ parameter must have a 'type' entry. (Ignored when site is
+ None)
:param data: The initializing data dict which is used for
:meth:`from_dict`
"""
self._site_set = False
- self._enabled = set()
- self._disabled = set()
+ self._enabled: Set[str] = set()
+ self._disabled: Set[str] = set()
self._set_site(site, module, param)
if data:
self.from_dict(data)
- def _set_site(self, site, module: str, param: str, *,
+ def _set_site(self,
+ site: pywikibot.site.APISite | None,
+ module: str | None,
+ param: str | None,
+ *,
clear_invalid: bool = False) -> None:
"""Set the site and valid names.
@@ -64,7 +70,6 @@
thrown.
:param site: The associated site
- :type site: pywikibot.site.APISite
:param module: The module name which is used by paraminfo.
:param param: The parameter name inside the module. That
parameter must have a 'type' entry.
@@ -80,6 +85,7 @@
self._valid_disable = set()
if site is None:
return
+
for type_value in site._paraminfo.parameter(module, param)['type']:
if type_value[0] == '!':
self._valid_disable.add(type_value[1:])
@@ -96,7 +102,7 @@
'"{}"'.format('", "'.join(invalid_names)))
self._site_set = True
- def from_dict(self, dictionary) -> None:
+ def from_dict(self, dictionary: dict[str, bool | None]) -> None:
"""Load options from the dict.
The options are not cleared before. If changes have been made
@@ -107,7 +113,6 @@
the value False, True or None. The names must be valid
depending on whether they enable or disable the option. All
names with the value None can be in either of the list.
- :type dictionary: dict (keys are strings, values are bool/None)
"""
enabled = set()
disabled = set()
diff --git a/pywikibot/data/api/_paraminfo.py b/pywikibot/data/api/_paraminfo.py
index 44fd7c4..beaf551 100644
--- a/pywikibot/data/api/_paraminfo.py
+++ b/pywikibot/data/api/_paraminfo.py
@@ -11,7 +11,7 @@
import pywikibot
from pywikibot import config
-from pywikibot.backports import Iterable, batched
+from pywikibot.backports import Dict, Iterable, Set, batched
from pywikibot.tools import (
classproperty,
deprecated,
@@ -36,6 +36,9 @@
init_modules = frozenset(['main', 'paraminfo'])
param_modules = ('list', 'meta', 'prop')
+ _action_modules: frozenset[str]
+ _modules: Dict[str, Set[str] | Dict[str, str]]
+
@remove_last_args(['modules_only_mode'])
def __init__(self,
site,
@@ -72,7 +75,7 @@
if self._action_modules:
assert modules == self._action_modules
else:
- self._action_modules = modules
+ self._action_modules = frozenset(modules)
elif name in self._modules:
# update required to updates from dict and set
self._modules[name].update(modules)
diff --git a/pywikibot/data/api/_requests.py b/pywikibot/data/api/_requests.py
index 035f65c..5fd6e11 100644
--- a/pywikibot/data/api/_requests.py
+++ b/pywikibot/data/api/_requests.py
@@ -1080,7 +1080,7 @@
error['help'] = error.pop('*') # formatversion 1
code = error.setdefault('code', 'Unknown')
- info = error.setdefault('info', None)
+ info = error.setdefault('info', '')
if (code == self.last_error['code']
and info == self.last_error['info']):
diff --git a/pywikibot/data/sparql.py b/pywikibot/data/sparql.py
index 4713888..ca5042d 100644
--- a/pywikibot/data/sparql.py
+++ b/pywikibot/data/sparql.py
@@ -1,19 +1,20 @@
"""SPARQL Query interface."""
#
-# (C) Pywikibot team, 2016-2024
+# (C) Pywikibot team, 2016-2025
#
# Distributed under the terms of the MIT license.
#
from __future__ import annotations
from textwrap import fill
+from typing import Any
from urllib.parse import quote
from requests import JSONDecodeError
from requests.exceptions import Timeout
from pywikibot import Site
-from pywikibot.backports import removeprefix
+from pywikibot.backports import Dict, removeprefix
from pywikibot.comms import http
from pywikibot.data import WaitingMixin
from pywikibot.exceptions import Error, NoUsernameError, ServerError
@@ -111,7 +112,7 @@
result = []
qvars = data['head']['vars']
for row in data['results']['bindings']:
- values = {}
+ values: Dict[str, Any] = {}
for var in qvars:
if var not in row:
# var is not available (OPTIONAL is probably used)
diff --git a/pywikibot/diff.py b/pywikibot/diff.py
index 052e934..c1ba3a8 100644
--- a/pywikibot/diff.py
+++ b/pywikibot/diff.py
@@ -1,6 +1,6 @@
"""Diff module."""
#
-# (C) Pywikibot team, 2014-2024
+# (C) Pywikibot team, 2014-2025
#
# Distributed under the terms of the MIT license.
#
@@ -9,7 +9,8 @@
import difflib
import math
from collections import abc
-from difflib import SequenceMatcher, _format_range_unified
+from difflib import _format_range_unified # type: ignore[attr-defined]
+from difflib import SequenceMatcher
from heapq import nlargest
from itertools import zip_longest
@@ -612,12 +613,14 @@
return comparands
-def get_close_matches_ratio(word: Sequence,
- possibilities: list[Sequence],
- *,
- n: int = 3,
- cutoff: float = 0.6,
- ignorecase: bool = False) -> list[float, Sequence]:
+def get_close_matches_ratio(
+ word: str,
+ possibilities: list[str],
+ *,
+ n: int = 3,
+ cutoff: float = 0.6,
+ ignorecase: bool = False
+) -> list[tuple[float, str]]:
"""Return a list of the best “good enough” matches and its ratio.
This method is similar to Python's :pylib:`difflib.get_close_matches()
diff --git a/pywikibot/page/_category.py b/pywikibot/page/_category.py
index f352394..ccc457e 100644
--- a/pywikibot/page/_category.py
+++ b/pywikibot/page/_category.py
@@ -61,7 +61,7 @@
def subcategories(self, *,
recurse: int | bool = False,
- **kwargs: Any) -> Generator[Page, None, None]:
+ **kwargs: Any) -> Generator[Category, None, None]:
"""Yield all subcategories of the current category.
**Usage:**
@@ -200,7 +200,7 @@
return
def members(self, *,
- recurse: bool = False,
+ recurse: int | bool = False,
total: int | None = None,
**kwargs: Any) -> Generator[Page, None, None]:
"""Yield all category contents (subcats, pages, and files).
diff --git a/pywikibot/page/_collections.py b/pywikibot/page/_collections.py
index b3b7984..dea73af 100644
--- a/pywikibot/page/_collections.py
+++ b/pywikibot/page/_collections.py
@@ -32,7 +32,7 @@
in subclasses.
"""
- def __init__(self, data=None) -> None:
+ def __init__(self, data: dict[str, Any] = None) -> None:
super().__init__()
self._data = {}
if data:
@@ -43,15 +43,15 @@
"""Construct a new empty BaseDataDict."""
return cls()
- def __getitem__(self, key):
+ def __getitem__(self, key: BaseSite | str) -> Any:
key = self.normalizeKey(key)
return self._data[key]
- def __setitem__(self, key, value) -> None:
+ def __setitem__(self, key: BaseSite | str, value: Any) -> None:
key = self.normalizeKey(key)
self._data[key] = value
- def __delitem__(self, key) -> None:
+ def __delitem__(self, key: BaseSite | str) -> None:
key = self.normalizeKey(key)
del self._data[key]
@@ -61,7 +61,7 @@
def __len__(self) -> int:
return len(self._data)
- def __contains__(self, key) -> bool:
+ def __contains__(self, key: BaseSite | str) -> bool:
key = self.normalizeKey(key)
return key in self._data
diff --git a/pywikibot/page/_user.py b/pywikibot/page/_user.py
index 206c1be..8f515e6 100644
--- a/pywikibot/page/_user.py
+++ b/pywikibot/page/_user.py
@@ -112,12 +112,13 @@
if force and hasattr(self, '_userprops'):
del self._userprops
if not hasattr(self, '_userprops'):
- self._userprops = list(self.site.users([self.username]))[0]
+ self._userprops = next(self.site.users([self.username]))
if self.isAnonymous() or self.is_CIDR():
- r = list(self.site.blocks(iprange=self.username, total=1))
+ r = next(self.site.blocks(iprange=self.username, total=1),
+ None)
if r:
- self._userprops['blockedby'] = r[0]['by']
- self._userprops['blockreason'] = r[0]['reason']
+ self._userprops['blockedby'] = r['by']
+ self._userprops['blockreason'] = r['reason']
return self._userprops
def registration(self,
diff --git a/pywikibot/pagegenerators/_factory.py
b/pywikibot/pagegenerators/_factory.py
index 249466d..ba4c648 100644
--- a/pywikibot/pagegenerators/_factory.py
+++ b/pywikibot/pagegenerators/_factory.py
@@ -62,13 +62,13 @@
if TYPE_CHECKING:
- from typing import Any, Literal
+ from typing import Any, Literal, Optional
from pywikibot.site import BaseSite, Namespace
HANDLER_GEN_TYPE = Iterable[pywikibot.page.BasePage]
GEN_FACTORY_CLAIM_TYPE = list[tuple[str, str, dict[str, str], bool]]
- OPT_GENERATOR_TYPE = HANDLER_GEN_TYPE | None
+ OPT_GENERATOR_TYPE = Optional[HANDLER_GEN_TYPE]
# This is the function that will be used to de-duplicate page iterators.
diff --git a/pywikibot/pagegenerators/_filters.py
b/pywikibot/pagegenerators/_filters.py
index cd006e2..641724d 100644
--- a/pywikibot/pagegenerators/_filters.py
+++ b/pywikibot/pagegenerators/_filters.py
@@ -1,6 +1,6 @@
"""Page filter generators provided by the pagegenerators module."""
#
-# (C) Pywikibot team, 2008-2024
+# (C) Pywikibot team, 2008-2025
#
# Distributed under the terms of the MIT license.
#
@@ -20,16 +20,18 @@
if TYPE_CHECKING:
+ from typing import Union
+
from pywikibot.site import BaseSite, Namespace
PRELOAD_SITE_TYPE = dict[pywikibot.site.BaseSite,
list[pywikibot.page.BasePage]]
- PATTERN_STR_OR_SEQ_TYPE = (
- str
- | Pattern[str]
- | Sequence[str]
- | Sequence[Pattern[str]]
- )
+ PATTERN_STR_OR_SEQ_TYPE = Union[
+ str,
+ Pattern[str],
+ Sequence[str],
+ Sequence[Pattern[str]],
+ ]
# This is the function that will be used to de-duplicate page iterators.
diff --git a/pywikibot/site/_extensions.py b/pywikibot/site/_extensions.py
index 7e8191d..8b15cf8 100644
--- a/pywikibot/site/_extensions.py
+++ b/pywikibot/site/_extensions.py
@@ -50,6 +50,11 @@
def simple_request(self, **kwargs) -> api.Request:
...
+ def querypage(
+ self, *args, **kwargs
+ ) -> Generator[tuple[pywikibot.Page, int], None, None]:
+ ...
+
class EchoMixin:
@@ -287,7 +292,7 @@
@need_extension('WikibaseClient')
def unconnected_pages(
- self,
+ self: BaseSiteProtocol,
total: int | None = None,
*,
strict: bool = False
@@ -305,7 +310,7 @@
:param strict: If ``True``, verify that each page still has no
data item before yielding it.
"""
- if total <= 0:
+ if total is not None and total <= 0:
return
if not strict:
@@ -329,9 +334,9 @@
@need_extension('Linter')
def linter_pages(
- self,
+ self: BaseSiteProtocol,
lint_categories=None,
- total: int = None,
+ total: int | None = None,
namespaces=None,
pageids: str | int | None = None,
lint_from: str | int | None = None
diff --git a/tox.ini b/tox.ini
index a949b3d..d47e26c 100644
--- a/tox.ini
+++ b/tox.ini
@@ -66,6 +66,7 @@
basepython = python3.9
deps =
pytest-mypy
+ types-PyMySQL
types-requests
commands =
mypy --version
--
To view, visit
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/1189971?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: I1f9000f9b33df83dda68b6c7734dfc0d5805442c
Gerrit-Change-Number: 1189971
Gerrit-PatchSet: 14
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]