jenkins-bot has submitted this change. ( https://gerrit.wikimedia.org/r/c/pywikibot/core/+/752359 )
Change subject: cleanup typing now that python 3.5.3+ is required ......................................................................
cleanup typing now that python 3.5.3+ is required
Change-Id: If21e4267e8b661ac56728fe43a905c52b3c11f32 --- M mypy.ini M pywikibot/__init__.py M pywikibot/_wbtypes.py M pywikibot/bot.py M pywikibot/bot_choice.py M pywikibot/exceptions.py M pywikibot/flow.py M pywikibot/interwiki_graph.py M pywikibot/logentries.py M pywikibot/logging.py M pywikibot/login.py M pywikibot/page/__init__.py M pywikibot/proofreadpage.py 13 files changed, 158 insertions(+), 193 deletions(-)
Approvals: Xqt: Looks good to me, approved jenkins-bot: Verified
diff --git a/mypy.ini b/mypy.ini index 228fda5..34102e5 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,4 +1,5 @@ [mypy] +python_version = 3.5 check_untyped_defs = true disallow_any_generics = true disallow_incomplete_defs = true diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py index 53e4c7f..04799fd 100644 --- a/pywikibot/__init__.py +++ b/pywikibot/__init__.py @@ -1,6 +1,6 @@ """The initialization file for the Pywikibot framework.""" # -# (C) Pywikibot team, 2008-2021 +# (C) Pywikibot team, 2008-2022 # # Distributed under the terms of the MIT license. # @@ -15,7 +15,7 @@ from contextlib import suppress from decimal import Decimal from queue import Queue -from typing import Any, Optional, Union +from typing import Any, Optional, Type, Union from urllib.parse import urlparse from warnings import warn
@@ -71,24 +71,9 @@ from pywikibot.tools import normalize_username from pywikibot.tools.formatter import color_format
-TO_DECIMAL_TYPE = Union[int, float, str, 'Decimal', None]
-# TODO: replace these after T286867 - -STR_OR_TIMESTAMP = Any # Union[str, 'Timestamp'] -OPT_STR_OR_SITE = Any # Union[str, 'pywikibot.site.BaseSite', None] -OPT_STR_OR_ITEM_PAGE = Any # Union[str, 'pywikibot.page.ItemPage', None] -OPT_STR_OR_FAMILY = Any # Union[str, 'pywikibot.family.Family', None] - -TIMESTAMP_CLASS = Any # Type['Timestamp'] -COORDINATE_CLASS = Any # Type['Coordinate'] -WB_TIME_CLASS = Any # Type['WbTime'] -WB_QUANTITY_CLASS = Any # Type['WbQuantity'] -WB_MONOLINGUAL_TEXT_CLASS = Any # Type['WbMonolingualText'] -WB_DATA_PAGE_CLASS = Any # Type['_WbDataPage'] -WB_GEO_SHAPE_CLASS = Any # Type['WbGeoShape'] -WB_TABULAR_DATA_CLASS = Any # Type['WbTabularData'] -WB_UNKNOWN_CLASS = Any # Type['WbUnknown'] +ItemPageStrNoneType = Union[str, 'ItemPage', None] +ToDecimalType = Union[int, float, str, 'Decimal', None]
__all__ = ( '__copyright__', '__description__', '__download_url__', '__license__', @@ -138,12 +123,12 @@ return self.replace(microsecond=self.microsecond)
@classproperty - def ISO8601Format(cls: TIMESTAMP_CLASS) -> str: + def ISO8601Format(cls: Type['Timestamp']) -> str: """ISO8601 format string class property for compatibility purpose.""" return cls._ISO8601Format()
@classmethod - def _ISO8601Format(cls: TIMESTAMP_CLASS, sep: str = 'T') -> str: + def _ISO8601Format(cls: Type['Timestamp'], sep: str = 'T') -> str: """ISO8601 format string.
:param sep: one-character separator, placed between the date and time @@ -153,7 +138,7 @@ return '%Y-%m-%d{}%H:%M:%SZ'.format(sep)
@classmethod - def fromISOformat(cls: TIMESTAMP_CLASS, ts: STR_OR_TIMESTAMP, + def fromISOformat(cls: Type['Timestamp'], ts: Union[str, 'Timestamp'], sep: str = 'T') -> 'Timestamp': """Convert an ISO 8601 timestamp to a Timestamp object.
@@ -168,7 +153,7 @@ return cls.strptime(ts, cls._ISO8601Format(sep))
@classmethod - def fromtimestampformat(cls: TIMESTAMP_CLASS, ts: STR_OR_TIMESTAMP + def fromtimestampformat(cls: Type['Timestamp'], ts: Union[str, 'Timestamp'] ) -> 'Timestamp': """Convert a MediaWiki internal timestamp to a Timestamp object.""" # If inadvertently passed a Timestamp object, use replace() @@ -228,7 +213,7 @@ globe: Optional[str] = None, typ: str = '', name: str = '', dim: Optional[int] = None, site: Optional[DataSite] = None, - globe_item: OPT_STR_OR_ITEM_PAGE = None, + globe_item: ItemPageStrNoneType = None, primary: bool = False) -> None: """ Represent a geo coordinate. @@ -295,7 +280,7 @@ }
@classmethod - def fromWikibase(cls: COORDINATE_CLASS, data: Dict[str, Any], + def fromWikibase(cls: Type['Coordinate'], data: Dict[str, Any], site: Optional[DataSite] = None) -> 'Coordinate': """ Constructor to create an object from Wikibase's JSON output. @@ -537,7 +522,7 @@ raise ValueError('Invalid precision: "{}"'.format(precision))
@classmethod - def fromTimestr(cls: WB_TIME_CLASS, + def fromTimestr(cls: Type['WbTime'], datetimestr: str, precision: Union[int, str] = 14, before: int = 0, @@ -575,7 +560,7 @@ precision, before, after, timezone, calendarmodel, site)
@classmethod - def fromTimestamp(cls: WB_TIME_CLASS, timestamp: 'Timestamp', + def fromTimestamp(cls: Type['WbTime'], timestamp: 'Timestamp', precision: Union[int, str] = 14, before: int = 0, after: int = 0, timezone: int = 0, calendarmodel: Optional[str] = None, @@ -643,7 +628,7 @@ return json
@classmethod - def fromWikibase(cls: WB_TIME_CLASS, data: Dict[str, Any], + def fromWikibase(cls: Type['WbTime'], data: Dict[str, Any], site: Optional[DataSite] = None) -> 'WbTime': """ Create a WbTime from the JSON data given by the Wikibase API. @@ -679,7 +664,7 @@ return site.mw_version < '1.29.0-wmf.2'
@staticmethod - def _todecimal(value: TO_DECIMAL_TYPE) -> Optional[Decimal]: + def _todecimal(value: ToDecimalType) -> Optional[Decimal]: """ Convert a string to a Decimal for use in WbQuantity.
@@ -704,10 +689,10 @@ """ return format(value, '+g') if value is not None else None
- def __init__(self, amount: TO_DECIMAL_TYPE, - unit: OPT_STR_OR_ITEM_PAGE = None, - error: Union[TO_DECIMAL_TYPE, - Tuple[TO_DECIMAL_TYPE, TO_DECIMAL_TYPE]] = None, + def __init__(self, amount: ToDecimalType, + unit: ItemPageStrNoneType = None, + error: Union[ToDecimalType, + Tuple[ToDecimalType, ToDecimalType]] = None, site: Optional[DataSite] = None) -> None: """ Create a new WbQuantity object. @@ -791,7 +776,7 @@ return json
@classmethod - def fromWikibase(cls: WB_QUANTITY_CLASS, data: Dict[str, Any], + def fromWikibase(cls: Type['WbQuantity'], data: Dict[str, Any], site: Optional[DataSite] = None) -> 'WbQuantity': """ Create a WbQuantity from the JSON data given by the Wikibase API. @@ -842,7 +827,7 @@ return json
@classmethod - def fromWikibase(cls: WB_MONOLINGUAL_TEXT_CLASS, data: Dict[str, Any], + def fromWikibase(cls: Type['WbMonolingualText'], data: Dict[str, Any], site: Optional[DataSite] = None) -> 'WbMonolingualText': """ Create a WbMonolingualText from the JSON data given by Wikibase API. @@ -865,7 +850,7 @@ _items = ('page', )
@classmethod - def _get_data_site(cls: WB_DATA_PAGE_CLASS, repo_site: DataSite + def _get_data_site(cls: Type['_WbDataPage'], repo_site: DataSite ) -> APISite: """ Return the site serving as a repository for a given data type. @@ -877,7 +862,7 @@ raise NotImplementedError
@classmethod - def _get_type_specifics(cls: WB_DATA_PAGE_CLASS, site: DataSite + def _get_type_specifics(cls: Type['_WbDataPage'], site: DataSite ) -> Dict[str, Any]: """ Return the specifics for a given data type. @@ -962,7 +947,7 @@ return self.page.title()
@classmethod - def fromWikibase(cls: WB_DATA_PAGE_CLASS, page_name: str, + def fromWikibase(cls: Type['_WbDataPage'], page_name: str, site: Optional[DataSite]) -> '_WbDataPage': """ Create a _WbDataPage from the JSON data given by the Wikibase API. @@ -983,7 +968,7 @@ """A Wikibase geo-shape representation."""
@classmethod - def _get_data_site(cls: WB_GEO_SHAPE_CLASS, site: DataSite) -> APISite: + def _get_data_site(cls: Type['WbGeoShape'], site: DataSite) -> APISite: """ Return the site serving as a geo-shape repository.
@@ -992,7 +977,7 @@ return site.geo_shape_repository()
@classmethod - def _get_type_specifics(cls: WB_GEO_SHAPE_CLASS, site: DataSite + def _get_type_specifics(cls: Type['WbGeoShape'], site: DataSite ) -> Dict[str, Any]: """ Return the specifics for WbGeoShape. @@ -1011,7 +996,7 @@ """A Wikibase tabular-data representation."""
@classmethod - def _get_data_site(cls: WB_TABULAR_DATA_CLASS, site: DataSite) -> APISite: + def _get_data_site(cls: Type['WbTabularData'], site: DataSite) -> APISite: """ Return the site serving as a tabular-data repository.
@@ -1020,7 +1005,7 @@ return site.tabular_data_repository()
@classmethod - def _get_type_specifics(cls: WB_TABULAR_DATA_CLASS, site: DataSite + def _get_type_specifics(cls: Type['WbTabularData'], site: DataSite ) -> Dict[str, Any]: """ Return the specifics for WbTabularData. @@ -1066,7 +1051,7 @@ return self.json
@classmethod - def fromWikibase(cls: WB_UNKNOWN_CLASS, data: Dict[str, Any], + def fromWikibase(cls: Type['WbUnknown'], data: Dict[str, Any], site: Optional[DataSite] = None) -> 'WbUnknown': """ Create a WbUnknown from the JSON data given by the Wikibase API. @@ -1112,9 +1097,9 @@
def Site(code: Optional[str] = None, - fam: OPT_STR_OR_FAMILY = None, + fam: Union[str, 'Family', None] = None, user: Optional[str] = None, *, - interface: OPT_STR_OR_SITE = None, + interface: Union[str, 'BaseSite', None] = None, url: Optional[str] = None) -> BaseSite: """A factory method to obtain a Site object.
diff --git a/pywikibot/_wbtypes.py b/pywikibot/_wbtypes.py index 641484f..863fad8 100644 --- a/pywikibot/_wbtypes.py +++ b/pywikibot/_wbtypes.py @@ -1,18 +1,17 @@ """Wikibase data type classes.""" # -# (C) Pywikibot team, 2013-2020 +# (C) Pywikibot team, 2013-2022 # # Distributed under the terms of the MIT license. # import abc import json -from typing import Any +from typing import Any, Optional
from pywikibot.backports import Dict
-# TODO: replace these after T286867 - -OPT_SITE = Any # Optional['pywikibot.site.DataSite'] +if False: # typing.TYPE_CHECKING + from pywikibot.site import DataSite
class WbRepresentation(abc.ABC): @@ -20,27 +19,30 @@ """Abstract class for Wikibase representations."""
@abc.abstractmethod - def __init__(self): + def __init__(self) -> None: """Constructor.""" raise NotImplementedError
@abc.abstractmethod - def toWikibase(self): + def toWikibase(self) -> Any: """Convert representation to JSON for the Wikibase API.""" raise NotImplementedError
@classmethod @abc.abstractmethod - def fromWikibase(cls, data: Dict[str, Any], site: OPT_SITE = None - ) -> 'WbRepresentation': + def fromWikibase( + cls, + data: Dict[str, Any], + site: Optional['DataSite'] = None + ) -> 'WbRepresentation': """Create a representation object based on JSON from Wikibase API.""" raise NotImplementedError
- def __str__(self): + def __str__(self) -> str: return json.dumps(self.toWikibase(), indent=4, sort_keys=True, separators=(',', ': '))
- def __repr__(self): + def __repr__(self) -> str: assert isinstance(self._items, tuple) assert all(isinstance(item, str) for item in self._items)
@@ -49,13 +51,13 @@ for attr, value in values) return '{}({})'.format(self.__class__.__name__, attrs)
- def __eq__(self, other): + def __eq__(self, other: object) -> bool: if isinstance(other, self.__class__): return self.toWikibase() == other.toWikibase() return NotImplemented
- def __hash__(self): + def __hash__(self) -> int: return hash(frozenset(self.toWikibase().items()))
- def __ne__(self, other): + def __ne__(self, other: object) -> bool: return not self.__eq__(other) diff --git a/pywikibot/bot.py b/pywikibot/bot.py index a3026d6..7181014 100644 --- a/pywikibot/bot.py +++ b/pywikibot/bot.py @@ -57,7 +57,7 @@ ``put_current`` is used. """ # -# (C) Pywikibot team, 2008-2021 +# (C) Pywikibot team, 2008-2022 # # Distributed under the terms of the MIT license. # @@ -181,18 +181,14 @@ from pywikibot.tools._logging import LoggingFormatter from pywikibot.tools.formatter import color_format
-ANSWER = Union[ - Tuple[str, str], - 'pywikibot.bot_choice.Option'] +if False: # typing.TYPE_CHECKING + from pywikibot.site import BaseSite
-ANSWER_TYPE = Union[Iterable[ANSWER], 'pywikibot.bot_choice.Option'] - -# TODO: We should change this to the following after T286867... - -PAGE_OR_LINK_TYPE = Any # Union['pywikibot.page.Link', 'pywikibot.page.Page'] -OPT_SITE_TYPE = Any # Optional['pywikibot.site.BaseSite'] -OPT_CLAIM_TYPE = Any # Optional['pywikibot.page.Claim'] -OPT_ITEM_PAGE_TYPE = Any # Optional['pywikibot.page.ItemPage'] +AnswerType = Union[ + Iterable[Union[Tuple[str, str], 'pywikibot.bot_choice.Option']], + 'pywikibot.bot_choice.Option', +] +PageLinkType = Union['pywikibot.page.Link', 'pywikibot.page.Page']
# Note: all output goes through python std library "logging" module _logger = 'bot' @@ -573,7 +569,7 @@
@initialize_handlers def input_choice(question: str, - answers: ANSWER_TYPE, + answers: AnswerType, default: Optional[str] = None, return_shortcut: bool = True, automatic_quit: bool = True, @@ -634,7 +630,7 @@
@initialize_handlers def input_list_choice(question: str, - answers: ANSWER_TYPE, + answers: AnswerType, default: Union[int, str, None] = None, force: bool = False) -> str: """ @@ -678,8 +674,8 @@ """
def __init__(self, - old_link: PAGE_OR_LINK_TYPE, - new_link: Union[PAGE_OR_LINK_TYPE, bool], + old_link: PageLinkType, + new_link: Union[PageLinkType, bool], default: Optional[str] = None, automatic_quit: bool = True) -> None: """ @@ -707,7 +703,7 @@ self._quit = automatic_quit
current_match_type = Optional[Tuple[ - PAGE_OR_LINK_TYPE, + PageLinkType, str, Mapping[str, str], Tuple[int, int] @@ -749,7 +745,7 @@
raise ValueError('Invalid choice "{}"'.format(choice))
- def __call__(self, link: PAGE_OR_LINK_TYPE, + def __call__(self, link: PageLinkType, text: str, groups: Mapping[str, str], rng: Tuple[int, int]) -> Any: """Ask user how the selected link should be replaced.""" @@ -815,7 +811,7 @@ return self.handle_answer(choice)
@property - def current_link(self) -> PAGE_OR_LINK_TYPE: + def current_link(self) -> PageLinkType: """Get the current link when it's handling one currently.""" if self._current_match is None: raise ValueError('No current link') @@ -1011,7 +1007,7 @@ module = import_module(module_name) # type: ignore help_text = module.__doc__ # type: str # type: ignore[assignment] if hasattr(module, 'docuReplacements'): - for key, value in module.docuReplacements.items(): # type: ignore + for key, value in module.docuReplacements.items(): help_text = help_text.replace(key, value.strip('\n\r')) except Exception: if module_name: @@ -1113,7 +1109,7 @@ i18n.input('pywikibot-enter-finished-browser')
-class _OptionDict(dict): +class _OptionDict(Dict[str, Any]):
"""The option dict which holds the options of OptionHandler.
@@ -1621,7 +1617,7 @@ instead which specifically handle multiple or single sites. """
- def __init__(self, site: OPT_SITE_TYPE = None, + def __init__(self, site: Optional['BaseSite'] = None, **kwargs: Any) -> None: """Create a Bot instance and initialize cached sites.""" # TODO: add warning if site is specified and generator @@ -1632,7 +1628,7 @@ super().__init__(**kwargs)
@property - def site(self) -> 'pywikibot.site.BaseSite': + def site(self) -> Optional['BaseSite']: """Get the current site.""" if not self._site: warning('Bot.site was not set before being retrieved.') @@ -1642,7 +1638,7 @@ return self._site
@site.setter - def site(self, site: OPT_SITE_TYPE) -> None: + def site(self, site: Optional['BaseSite']) -> None: """ Set the Site that the bot is using.
@@ -1704,7 +1700,7 @@ """
def __init__(self, - site: Union[OPT_SITE_TYPE, bool] = True, + site: Union['BaseSite', bool, None] = True, **kwargs: Any) -> None: """ Create a SingleSiteBot instance. @@ -1713,7 +1709,7 @@ pywikibot.Site. """ if site is True: - self._site = pywikibot.Site() + self._site = pywikibot.Site() # type: Optional[BaseSite] elif site is False: raise ValueError("'site' must be a site, True, or None") else: @@ -1721,14 +1717,14 @@ super().__init__(**kwargs)
@property - def site(self) -> OPT_SITE_TYPE: + def site(self) -> 'BaseSite': """Site that the bot is using.""" if not self._site: raise ValueError('The site has not been defined yet.') return self._site
@site.setter - def site(self, value: OPT_SITE_TYPE) -> None: + def site(self, value: Optional['BaseSite']) -> None: """Set the current site but warns if different.""" if self._site: # Warn in any case where the site is (probably) changed after @@ -2108,7 +2104,7 @@
def user_add_claim(self, item: 'pywikibot.page.ItemPage', claim: 'pywikibot.page.Claim', - source: OPT_SITE_TYPE = None, + source: Optional['BaseSite'] = None, bot: bool = True, **kwargs: Any) -> bool: """ Add a claim to an item, with user confirmation as required. @@ -2134,8 +2130,7 @@ claim.getTarget())) return self._save_page(item, item.addClaim, claim, bot=bot, **kwargs)
- def getSource(self, site: 'pywikibot.site.BaseSite' - ) -> OPT_CLAIM_TYPE: + def getSource(self, site: 'BaseSite') -> Optional['pywikibot.page.Claim']: """ Create a Claim usable as a source for Wikibase statements.
@@ -2154,7 +2149,7 @@ self, item: 'pywikibot.page.ItemPage', claim: 'pywikibot.page.Claim', exists_arg: str = '', - source: OPT_SITE_TYPE = None, + source: Optional['BaseSite'] = None, logger_callback: Callable[[str], Any] = log, **kwargs: Any) -> bool: """ @@ -2227,7 +2222,7 @@ data: Optional[Dict[str, Any]] = None, summary: Optional[str] = None, **kwargs: Any - ) -> OPT_ITEM_PAGE_TYPE: + ) -> Optional['pywikibot.page.ItemPage']: """ Create an ItemPage with the provided page as the sitelink.
diff --git a/pywikibot/bot_choice.py b/pywikibot/bot_choice.py index 822fe9d..9fb1660 100755 --- a/pywikibot/bot_choice.py +++ b/pywikibot/bot_choice.py @@ -1,6 +1,6 @@ """Options and Choices for :py:meth:`pywikibot.input_choice`.""" # -# (C) Pywikibot team, 2015-2021 +# (C) Pywikibot team, 2015-2022 # # Distributed under the terms of the MIT license. # @@ -16,10 +16,6 @@
from pywikibot.tools import deprecated, issue_deprecation_warning
-# TODO: replace these after T286867 - -OPT_REPLACE_TYPE = Any # Optional['pywikibot.bot.InteractiveReplace'] -
class Option(ABC):
@@ -281,14 +277,18 @@
"""A simple choice consisting of an option, shortcut and handler."""
- def __init__(self, option: str, shortcut: str, - replacer: OPT_REPLACE_TYPE) -> None: + def __init__( + self, + option: str, + shortcut: str, + replacer: Optional['pywikibot.bot.InteractiveReplace'] + ) -> None: """Initializer.""" super().__init__(option, shortcut) self._replacer = replacer
@property - def replacer(self) -> OPT_REPLACE_TYPE: + def replacer(self) -> Optional['pywikibot.bot.InteractiveReplace']: """The replacer.""" return self._replacer
@@ -320,8 +320,14 @@
"""A choice returning a mix of the link new and current link."""
- def __init__(self, option: str, shortcut: str, replacer: OPT_REPLACE_TYPE, - replace_section: bool, replace_label: bool) -> None: + def __init__( + self, + option: str, + shortcut: str, + replacer: Optional['pywikibot.bot.InteractiveReplace'], + replace_section: bool, + replace_label: bool + ) -> None: """Initializer.""" super().__init__(option, shortcut, replacer) self._section = replace_section @@ -361,7 +367,7 @@
"""Add an option to always apply the default."""
- def __init__(self, replacer: OPT_REPLACE_TYPE, + def __init__(self, replacer: Optional['pywikibot.bot.InteractiveReplace'], option: str = 'always', shortcut: str = 'a') -> None: """Initializer.""" super().__init__(option, shortcut, replacer) diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py index ce08846..211c6c1 100644 --- a/pywikibot/exceptions.py +++ b/pywikibot/exceptions.py @@ -281,7 +281,7 @@ # Override this in subclasses. message = ''
- def __init__(self, page: 'pywikibot.page.Page', + def __init__(self, page: 'pywikibot.page.BasePage', message: Optional[str] = None) -> None: """ Initializer. diff --git a/pywikibot/flow.py b/pywikibot/flow.py index 855a45e..88f7082 100644 --- a/pywikibot/flow.py +++ b/pywikibot/flow.py @@ -1,13 +1,13 @@ """Objects representing Flow entities, like boards, topics, and posts.""" # -# (C) Pywikibot team, 2015-2021 +# (C) Pywikibot team, 2015-2022 # # Distributed under the terms of the MIT license. # import abc import datetime import logging -from typing import Any, Union +from typing import Any, Type, Union from urllib.parse import parse_qs, urlparse
import pywikibot @@ -17,20 +17,11 @@ NoPageError, UnknownExtensionError, ) -from pywikibot.page import BasePage, User +from pywikibot.page import BasePage, PageSourceType, User
logger = logging.getLogger('pywiki.wiki.flow')
-# TODO: replace these after T286867 - -TOPIC_CLASS_TYPE = Any # Type['Topic'] - -# Union['pywikibot.site.BaseSite', 'pywikibot.page.Link', -# 'pywikibot.page.Page'] - -SOURCE_TYPE = Any -
# Flow page-like objects (boards and topics) class FlowPage(BasePage, abc.ABC): @@ -40,7 +31,7 @@ It cannot be instantiated directly. """
- def __init__(self, source: SOURCE_TYPE, title: str = '') -> None: + def __init__(self, source: PageSourceType, title: str = '') -> None: """Initializer.
:param source: A Flow-enabled site or a Link or Page on such a site @@ -178,7 +169,7 @@ self.root._load(load_from_topic=True)
@classmethod - def create_topic(cls: TOPIC_CLASS_TYPE, board: 'Board', title: str, + def create_topic(cls: Type['Topic'], board: 'Board', title: str, content: str, content_format: str = 'wikitext' ) -> 'Topic': """Create and return a Topic object for a new topic on a Board. @@ -195,7 +186,7 @@ return cls(board.site, data['topic-page'])
@classmethod - def from_topiclist_data(cls: TOPIC_CLASS_TYPE, board: 'Board', + def from_topiclist_data(cls: Type['Topic'], board: 'Board', root_uuid: str, topiclist_data: Dict[str, Any]) -> 'Topic': """Create a Topic object from API data. @@ -422,7 +413,7 @@ return self._current_revision['isModerated']
@property - def creator(self) -> str: + def creator(self) -> User: """The creator of this post.""" if not hasattr(self, '_current_revision'): self._load() diff --git a/pywikibot/interwiki_graph.py b/pywikibot/interwiki_graph.py index 8d8e50d..e9ca5c6 100644 --- a/pywikibot/interwiki_graph.py +++ b/pywikibot/interwiki_graph.py @@ -1,24 +1,18 @@ """Module with the Graphviz drawing calls.""" # -# (C) Pywikibot team, 2006-2021 +# (C) Pywikibot team, 2006-2022 # # Distributed under the terms of the MIT license. # import itertools import threading from collections import Counter -from typing import Any, Optional +from typing import Optional
import pywikibot from pywikibot import config from pywikibot.backports import Dict, List, Set
-FOUND_IN_TYPE = Dict['pywikibot.page.Page', List['pywikibot.page.Page']] - -# TODO: replace these after T286867 - -OPT_PAGE_TYPE = Any # Optional['pywikibot.page.Page'] - try: import pydot PYDOT_ERROR = None @@ -26,6 +20,9 @@ PYDOT_ERROR = e
+FoundInType = Dict['pywikibot.page.Page', List['pywikibot.page.Page']] + + class GraphSavingThread(threading.Thread):
""" @@ -61,7 +58,7 @@
"""Data about a page with translations on multiple wikis."""
- def __init__(self, origin: OPT_PAGE_TYPE = None) -> None: + def __init__(self, origin: Optional['pywikibot.page.Page'] = None) -> None: """Initializer.
:param origin: the page on the 'origin' wiki @@ -73,17 +70,17 @@ # 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: FOUND_IN_TYPE + self.found_in = {} # type: FoundInType if origin: self.found_in = {origin: []}
@property - def origin(self) -> OPT_PAGE_TYPE: + def origin(self) -> Optional['pywikibot.page.Page']: """Page on the origin wiki.""" return self._origin
@origin.setter - def origin(self, value: OPT_PAGE_TYPE) -> None: + def origin(self, value: Optional['pywikibot.page.Page']) -> None: self._origin = value
diff --git a/pywikibot/logentries.py b/pywikibot/logentries.py index f2c356d..3df2838 100644 --- a/pywikibot/logentries.py +++ b/pywikibot/logentries.py @@ -1,13 +1,13 @@ """Objects representing Mediawiki log entries.""" # -# (C) Pywikibot team, 2007-2021 +# (C) Pywikibot team, 2007-2022 # # Distributed under the terms of the MIT license. # from collections import UserDict from contextlib import suppress import datetime -from typing import Any, Optional +from typing import Any, Optional, Type, Union
import pywikibot from pywikibot.backports import Dict, List, Tuple @@ -16,14 +16,6 @@
_logger = 'wiki'
-# TODO: replace these after T286867 - -PAGE_OR_INT_TYPE = Any # Union[int, 'pywikibot.page.Page'] -OPT_TIMESTAMP_TYPE = Any # Optional['pywikibot.Timestamp'] - -LOG_ENTRY_CLASS = Any # Type['LogEntry'] -LOG_ENTRY_FACTORY_CLASS = Any # Type['LogEntryFactory'] -
class LogEntry(UserDict):
@@ -107,7 +99,7 @@
return self[self._expected_type]
- def page(self) -> PAGE_OR_INT_TYPE: + def page(self) -> Union[int, 'pywikibot.page.Page']: """ Page on which action was performed.
@@ -169,7 +161,7 @@ if self.isAutoblockRemoval: self._blockid = int(self['title'][pos + 1:])
- def page(self) -> PAGE_OR_INT_TYPE: + def page(self) -> Union[int, 'pywikibot.page.Page']: """ Return the blocked account or IP.
@@ -217,7 +209,7 @@ self._duration = self.expiry() - self.timestamp() return self._duration
- def expiry(self) -> OPT_TIMESTAMP_TYPE: + def expiry(self) -> Optional['pywikibot.Timestamp']: """Return a Timestamp representing the block expiry date.""" if not hasattr(self, '_expiry'): details = self._params.get('expiry') @@ -374,7 +366,7 @@ """ return self._creator(logdata)
- def get_valid_entry_class(self, logtype: str) -> LOG_ENTRY_CLASS: + def get_valid_entry_class(self, logtype: str) -> 'LogEntry': """ Return the class corresponding to the @logtype string parameter.
@@ -387,8 +379,8 @@ return LogEntryFactory.get_entry_class(logtype)
@classmethod - def get_entry_class(cls: LOG_ENTRY_FACTORY_CLASS, - logtype: str) -> LOG_ENTRY_CLASS: + def get_entry_class(cls: Type['LogEntryFactory'], + logtype: str) -> 'LogEntry': """ Return the class corresponding to the @logtype string parameter.
@@ -398,7 +390,7 @@ or use the get_valid_entry_class instance method instead. """ if logtype not in cls._logtypes: - bases = (OtherLogEntry, ) # type: Tuple[LOG_ENTRY_CLASS, ...] + bases = (OtherLogEntry, ) # type: Tuple['LogEntry', ...] if logtype in ('newusers', 'thanks'): bases = (UserTargetLogEntry, OtherLogEntry)
diff --git a/pywikibot/logging.py b/pywikibot/logging.py index 31a8726..7c2edab 100644 --- a/pywikibot/logging.py +++ b/pywikibot/logging.py @@ -11,7 +11,7 @@
# logging levels from logging import CRITICAL, DEBUG, ERROR, INFO, WARNING -from typing import AnyStr, Optional +from typing import Optional
from pywikibot.backports import Callable, List
@@ -62,7 +62,7 @@ # string indicating the debugging layer.
-def logoutput(text: Any, decoder: Optional[str] = None, +def logoutput(text: object, decoder: Optional[str] = None, newline: bool = True, _level: int = INFO, _logger: str = '', **kwargs: Any) -> None: """Format output and send to the logging module. @@ -108,7 +108,7 @@ logger.log(_level, decoded_text, extra=context, **kwargs)
-def output(text: AnyStr, decoder: Optional[str] = None, newline: bool = True, +def output(text: object, decoder: Optional[str] = None, newline: bool = True, **kwargs: Any) -> None: r"""Output a message to the user via the userinterface.
@@ -133,7 +133,7 @@ logoutput(text, decoder, newline, INFO, **kwargs)
-def stdout(text: AnyStr, decoder: Optional[str] = None, newline: bool = True, +def stdout(text: object, decoder: Optional[str] = None, newline: bool = True, **kwargs: Any) -> None: """Output script results to the user via the userinterface.
@@ -151,7 +151,7 @@ logoutput(text, decoder, newline, STDOUT, **kwargs)
-def warning(text: AnyStr, decoder: Optional[str] = None, +def warning(text: object, decoder: Optional[str] = None, newline: bool = True, **kwargs: Any) -> None: """Output a warning message to the user via the userinterface.
@@ -165,7 +165,7 @@ logoutput(text, decoder, newline, WARNING, **kwargs)
-def error(text: AnyStr, decoder: Optional[str] = None, newline: bool = True, +def error(text: object, decoder: Optional[str] = None, newline: bool = True, **kwargs: Any) -> None: """Output an error message to the user via the userinterface.
@@ -179,7 +179,7 @@ logoutput(text, decoder, newline, ERROR, **kwargs)
-def log(text: AnyStr, decoder: Optional[str] = None, newline: bool = True, +def log(text: object, decoder: Optional[str] = None, newline: bool = True, **kwargs: Any) -> None: """Output a record to the log file.
@@ -193,7 +193,7 @@ logoutput(text, decoder, newline, VERBOSE, **kwargs)
-def critical(text: AnyStr, decoder: Optional[str] = None, newline: bool = True, +def critical(text: object, decoder: Optional[str] = None, newline: bool = True, **kwargs: Any) -> None: """Output a critical record to the user via the userinterface.
@@ -207,7 +207,7 @@ logoutput(text, decoder, newline, CRITICAL, **kwargs)
-def debug(text: AnyStr, layer: str, decoder: Optional[str] = None, +def debug(text: object, layer: str, decoder: Optional[str] = None, newline: bool = True, **kwargs: Any) -> None: """Output a debug record to the log file.
diff --git a/pywikibot/login.py b/pywikibot/login.py index 5be39a1..c52568f 100644 --- a/pywikibot/login.py +++ b/pywikibot/login.py @@ -28,10 +28,6 @@ except ImportError as e: mwoauth = e
-# TODO: replace these after T286867 - -OPT_SITE_TYPE = Any # Optional['pywikibot.site.BaseSite'] -
class _PasswordFileWarning(UserWarning):
@@ -86,7 +82,7 @@ """Site login manager."""
def __init__(self, password: Optional[str] = None, - site: OPT_SITE_TYPE = None, + site: Optional['pywikibot.site.BaseSite'] = None, user: Optional[str] = None) -> None: """ Initializer. @@ -364,7 +360,7 @@ # authentication process
def __init__(self, password: Optional[str] = None, - site: OPT_SITE_TYPE = None, + site: Optional['pywikibot.site.BaseSite'] = None, user: Optional[str] = None) -> None: """ Initializer. diff --git a/pywikibot/page/__init__.py b/pywikibot/page/__init__.py index eeb2d6f..69df5ea 100644 --- a/pywikibot/page/__init__.py +++ b/pywikibot/page/__init__.py @@ -11,7 +11,7 @@
""" # -# (C) Pywikibot team, 2008-2021 +# (C) Pywikibot team, 2008-2022 # # Distributed under the terms of the MIT license. # @@ -101,6 +101,12 @@ 'url2unicode', )
+PageSourceType = Union[ + 'pywikibot.site.BaseLink', + 'pywikibot.page.BaseSite', + 'pywikibot.page.Page', +] + logger = logging.getLogger('pywiki.wiki.page')
diff --git a/pywikibot/proofreadpage.py b/pywikibot/proofreadpage.py index 7c1eed4..7096f32 100644 --- a/pywikibot/proofreadpage.py +++ b/pywikibot/proofreadpage.py @@ -33,7 +33,7 @@ from http import HTTPStatus from typing import Any, Optional, Union
-from requests.exceptions import ReadTimeout # type: ignore[import] +from requests.exceptions import ReadTimeout
import pywikibot from pywikibot import textlib @@ -49,16 +49,7 @@ from pywikibot.comms import http from pywikibot.data.api import Request from pywikibot.exceptions import Error, OtherPageSaveError - -PAGES_FROM_LABEL_TYPE = Dict[str, Set['pywikibot.page.Page']] - -# TODO: replace these after T286867 - -OPT_INDEX_PAGE_TYPE = Any # Optional['IndexPage'] -OPT_INDEX_PAGE_LIST_TYPE = Any # Optional[List['IndexPage']] - -INDEX_TYPE = Optional[Tuple[OPT_INDEX_PAGE_TYPE, - OPT_INDEX_PAGE_LIST_TYPE]] +from pywikibot.page import PageSourceType
try: from bs4 import BeautifulSoup @@ -81,6 +72,10 @@ _logger = 'proofreadpage'
+PagesFromLabelType = Dict[str, Set['pywikibot.page.Page']] +_IndexType = Tuple[Optional['IndexPage'], List['IndexPage']] + + def decompose(fn: Callable) -> Callable: # type: ignore # noqa: N805 """Decorator for ProofreadPage.
@@ -198,7 +193,7 @@ } _OCR_METHODS = list(_OCR_CMDS.keys())
- def __init__(self, source, title: str = '') -> None: + def __init__(self, source: PageSourceType, title: str = '') -> None: """Instantiate a ProofreadPage object.
:raise UnknownExtensionError: source Site has no ProofreadPage @@ -263,7 +258,7 @@ return base, ext, num
@property - def index(self) -> OPT_INDEX_PAGE_TYPE: + def index(self) -> Optional['IndexPage']: """Get the Index page which contains ProofreadPage.
If there are many Index pages link to this ProofreadPage, and @@ -281,7 +276,7 @@ set(self.getReferences(namespaces=index_ns))]
if not what_links_here: - self._index = (None, []) + self._index = (None, []) # type: _IndexType elif len(what_links_here) == 1: self._index = (what_links_here.pop(), []) else: @@ -294,28 +289,28 @@ self._index = (page, what_links_here) break
- page, others = self._index + index_page, others = self._index if others: pywikibot.warning('{} linked to several Index pages.'.format(self)) - pywikibot.output('{}{!s}'.format(' ' * 9, [page] + others)) + pywikibot.output('{}{!s}'.format(' ' * 9, [index_page] + others))
- if page: + if index_page: pywikibot.output( - '{}Selected Index: {}'.format(' ' * 9, page)) + '{}Selected Index: {}'.format(' ' * 9, index_page)) pywikibot.output('{}remaining: {!s}'.format(' ' * 9, others))
- if not page: + if not index_page: pywikibot.warning('Page {} is not linked to any Index page.' .format(self))
- return page + return index_page
@index.setter def index(self, value: 'IndexPage') -> None: if not isinstance(value, IndexPage): raise TypeError('value {} must be an IndexPage object.' .format(value)) - self._index = (value, None) + self._index = (value, [])
@index.deleter def index(self) -> None: @@ -453,7 +448,7 @@ """ # Text is already cached. if getattr(self, '_text', None) is not None: - return self._text + return self._text # type: ignore[return-value]
if self.exists(): # If page exists, load it. @@ -547,13 +542,12 @@
def save(self, *args: Any, **kwargs: Any) -> None: # See Page.save(). """Save page content after recomposing the page.""" - summary = kwargs.pop('summary', '') - summary = self.pre_summary + summary + kwargs['summary'] = self.pre_summary + kwargs.get('summary', '') # Save using contentformat='application/json'. kwargs['contentformat'] = 'application/json' kwargs['contentmodel'] = 'proofread-page' text = self._page_to_json() - super().save(*args, text=text, summary=summary, **kwargs) + super().save(*args, text=text, **kwargs)
@property def pre_summary(self) -> str: @@ -649,7 +643,7 @@ pywikibot.warning('retrying in {} seconds ...'.format(retry)) time.sleep(retry) else: - return True, ReadTimeout + return True, ReadTimeout('ReadTimeout: Could not perform OCR')
if HTTPStatus.BAD_REQUEST <= response.status_code < 600: return True, 'Http response status {}'.format(response.status_code) @@ -797,7 +791,7 @@
INDEX_TEMPLATE = ':MediaWiki:Proofreadpage_index_template'
- def __init__(self, source, title: str = '') -> None: + def __init__(self, source: PageSourceType, title: str = '') -> None: """Instantiate an IndexPage object.
In this class: @@ -900,7 +894,7 @@ 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: PAGES_FROM_LABEL_TYPE + 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] if hasattr(self, '_parsed_text'):
pywikibot-commits@lists.wikimedia.org