jenkins-bot submitted this change.
[IMPR] bot_choice.py type hints
Bug: T286403
Change-Id: Ia04380cf787aac4b92d17b81b3baa87ba9764243
---
M pywikibot/bot_choice.py
1 file changed, 86 insertions(+), 68 deletions(-)
diff --git a/pywikibot/bot_choice.py b/pywikibot/bot_choice.py
index c1f6a7b..89daa17 100755
--- a/pywikibot/bot_choice.py
+++ b/pywikibot/bot_choice.py
@@ -8,10 +8,12 @@
from abc import ABC, abstractmethod
from textwrap import fill
-from typing import Optional
+from typing import Any, Optional
import pywikibot
+from pywikibot.backports import Iterable, Sequence
+
from pywikibot.tools import (
deprecated,
deprecated_args,
@@ -38,18 +40,20 @@
True for the same value.
"""
- def __init__(self, stop=True) -> None:
+ def __init__(self, stop: bool = True) -> None:
"""Initializer."""
self._stop = stop
@staticmethod
- def formatted(text: str, options, default=None) -> str:
+ def formatted(text: str, options: Iterable['Option'],
+ default: Optional[str] = None) -> str:
"""
Create a text with the options formatted into it.
:param text: Text into which options are to be formatted
:param options: Option instances to be formatted
- :type options: Iterable
+ :param default: filler for any option's 'default' placeholder
+
:return: Text with the options formatted into it
"""
formatted_options = []
@@ -66,26 +70,24 @@
"""Return whether this option stops asking."""
return self._stop
- def handled(self, value):
+ def handled(self, value: str) -> Optional['Option']:
"""
Return the Option object that applies to the given value.
If this Option object doesn't know which applies it returns None.
"""
- if self.test(value):
- return self
- return None
+ return self if self.test(value) else None
- def format(self, default=None):
+ def format(self, default: Optional[str] = None) -> str:
"""Return a formatted string for that option."""
raise NotImplementedError()
- def test(self, value):
+ def test(self, value: str) -> bool:
"""Return True whether this option applies."""
raise NotImplementedError()
@abstractmethod
- def result(self, value):
+ def result(self, value: str) -> Any:
"""Return the actual value which is associated by the given one.
*New in version 6.2:* *result()* is an abstract method and must
@@ -105,11 +107,11 @@
before_question = False
@property
- def stop(self):
+ def stop(self) -> bool:
"""Never stop asking."""
return False
- def result(self, value):
+ def result(self, value: str) -> Any:
"""Just return None."""
return None
@@ -123,7 +125,7 @@
"""
return ''
- def output(self):
+ def output(self) -> None:
"""Output string when selected and possibly before the question.
:Note: This method should never be overridden.
@@ -135,7 +137,7 @@
"""An option with a description and shortcut and returning the shortcut."""
- def __init__(self, option: str, shortcut: str, **kwargs):
+ def __init__(self, option: str, shortcut: str, **kwargs) -> None:
"""
Initializer.
@@ -146,7 +148,7 @@
self.option = option
self.shortcut = shortcut.lower()
- def format(self, default=None) -> str:
+ def format(self, default: Optional[str] = None) -> str:
"""Return a formatted string for that option."""
index = self.option.lower().find(self.shortcut)
shortcut = self.shortcut
@@ -158,11 +160,11 @@
self.option[index + len(self.shortcut):])
return '{} [{}]'.format(self.option, shortcut)
- def result(self, value):
+ def result(self, value: str) -> Any:
"""Return the lowercased shortcut."""
return self.shortcut
- def test(self, value) -> bool:
+ def test(self, value: str) -> bool:
"""Return True whether this option applies."""
return (self.shortcut.lower() == value.lower()
or self.option.lower() == value.lower())
@@ -172,7 +174,8 @@
"""An option which calls out property of the given output class."""
- def __init__(self, option, shortcut, output, **kwargs):
+ def __init__(self, option: str, shortcut: str, output: OutputOption,
+ **kwargs) -> None:
"""Create a new option for the given sequence."""
super().__init__(option, shortcut, **kwargs)
self._outputter = output
@@ -197,18 +200,19 @@
option applies while handle returns the sub option.
"""
- def __init__(self, option, shortcut, description, options):
+ def __init__(self, option: str, shortcut: str, description: str,
+ options: Iterable[Option]) -> None:
"""Initializer."""
super().__init__(option, shortcut, stop=False)
self.description = description
self.options = options
- def format(self, default=None):
+ def format(self, default: Optional[str] = None) -> str:
"""Return a formatted string for that option."""
self._output = Option.formatted(self.description, self.options)
return super().format(default=default)
- def handled(self, value):
+ def handled(self, value: str) -> Optional[Option]:
"""Return itself if it applies or the applying sub option."""
for option in self.options:
handled = option.handled(value)
@@ -218,7 +222,7 @@
return super().handled(value)
@property
- def out(self):
+ def out(self) -> str:
"""Output of suboptions."""
return self._output
@@ -227,9 +231,8 @@
"""An option to show more and more context."""
- def __init__(
- self, option, shortcut, text, context, delta=100, start=0, end=0
- ):
+ def __init__(self, option: str, shortcut: str, text: str, context: int,
+ delta: int = 100, start: int = 0, end: int = 0) -> None:
"""Initializer."""
super().__init__(option, shortcut, stop=False)
self.text = text
@@ -238,13 +241,13 @@
self.start = start
self.end = end
- def result(self, value):
+ def result(self, value: str) -> Any:
"""Add the delta to the context and output it."""
self.context += self.delta
super().result(value)
@property
- def out(self):
+ def out(self) -> str:
"""Output section of the text."""
start = max(0, self.start - self.context)
end = min(len(self.text), self.end + self.context)
@@ -252,7 +255,7 @@
@deprecated_args(start_context='start', end_context='end')
@deprecated('pywikibot.output(ContextOption.out)', since='6.2.0')
- def output_range(self, start, end):
+ def output_range(self, start: int, end: int) -> None:
"""DEPRECATED. Output a section from the text."""
pywikibot.output(self.text[start:end])
@@ -261,22 +264,24 @@
"""A simple choice consisting of an option, shortcut and handler."""
- def __init__(self, option, shortcut, replacer):
+ 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):
+ def replacer(self) -> Optional['pywikibot.bot.InteractiveReplace']:
"""The replacer."""
return self._replacer
@abstractmethod
- def handle(self):
+ def handle(self) -> Any:
"""Handle this choice. Must be implemented."""
raise NotImplementedError()
- def handle_link(self):
+ def handle_link(self) -> bool:
"""The current link will be handled by this choice."""
return False
@@ -285,12 +290,12 @@
"""A static choice which just returns the given value."""
- def __init__(self, option, shortcut, result):
+ def __init__(self, option: str, shortcut: str, result: Any) -> None:
"""Create instance with replacer set to None."""
super().__init__(option, shortcut, None)
self._result = result
- def handle(self):
+ def handle(self) -> Any:
"""Return the predefined value."""
return self._result
@@ -299,15 +304,20 @@
"""A choice returning a mix of the link new and current link."""
- def __init__(self, option, shortcut, replacer, replace_section,
- replace_label):
+ 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
self._label = replace_label
- def handle(self):
+ def handle(self) -> Any:
"""Handle by either applying the new section or label."""
+ if not self.replacer:
+ raise ValueError('LinkChoice requires a replacer')
+
kwargs = {}
if self._section:
kwargs['section'] = self.replacer._new.section
@@ -337,23 +347,27 @@
"""Add an option to always apply the default."""
- def __init__(self, replacer, option='always', shortcut='a'):
+ def __init__(self, replacer: Optional['pywikibot.bot.InteractiveReplace'],
+ option: str = 'always', shortcut: str = 'a') -> None:
"""Initializer."""
super().__init__(option, shortcut, replacer)
self.always = False
- def handle(self):
+ def handle(self) -> Any:
"""Handle the custom shortcut."""
self.always = True
return self.answer
- def handle_link(self):
+ def handle_link(self) -> bool:
"""Directly return answer whether it's applying it always."""
return self.always
@property
- def answer(self):
+ def answer(self) -> Any:
"""Get the actual default answer instructing the replacement."""
+ if not self.replacer:
+ raise ValueError('AlwaysChoice requires a replacer')
+
return self.replacer.handle_answer(self.replacer._default)
@@ -361,7 +375,8 @@
"""An option allowing a range of integers."""
- def __init__(self, minimum=1, maximum=None, prefix='', **kwargs):
+ def __init__(self, minimum: int = 1, maximum: Optional[int] = None,
+ prefix: str = '', **kwargs) -> None:
"""Initializer."""
super().__init__(**kwargs)
if not ((minimum is None or isinstance(minimum, int))
@@ -374,34 +389,36 @@
self._max = maximum
self.prefix = prefix
- def test(self, value) -> bool:
+ def test(self, value: str) -> bool:
"""Return whether the value is an int and in the specified range."""
try:
- value = self.parse(value)
+ int_value = self.parse(value)
except ValueError:
return False
- return ((self.minimum is None or value >= self.minimum)
- and (self.maximum is None or value <= self.maximum))
+ return ((self.minimum is None or int_value >= self.minimum)
+ and (self.maximum is None or int_value <= self.maximum))
@property
- def minimum(self):
+ def minimum(self) -> int:
"""Return the lower bound of the range of allowed values."""
return self._min
@property
- def maximum(self):
+ def maximum(self) -> Optional[int]:
"""Return the upper bound of the range of allowed values."""
return self._max
- def format(self, default=None) -> str:
+ def format(self, default: Optional[str] = None) -> str:
"""Return a formatted string showing the range."""
+ value = None # type: Optional[int]
+
if default is not None and self.test(default):
value = self.parse(default)
default = '[{}]'.format(value)
else:
- value = None
default = ''
+
if self.minimum is not None or self.maximum is not None:
if default and value == self.minimum:
minimum = default
@@ -422,14 +439,14 @@
rng = 'any' + default
return '{}<number> [{}]'.format(self.prefix, rng)
- def parse(self, value) -> int:
+ def parse(self, value: str) -> int:
"""Return integer from value with prefix removed."""
if value.lower().startswith(self.prefix.lower()):
return int(value[len(self.prefix):])
raise ValueError('Value does not start with prefix')
- def result(self, value):
- """Return the value converted into int."""
+ def result(self, value: str) -> Any:
+ """Return a tuple with the prefix and value converted into an int."""
return self.prefix, self.parse(value)
@@ -437,7 +454,7 @@
"""An option to select something from a list."""
- def __init__(self, sequence, prefix='', **kwargs):
+ def __init__(self, sequence: Sequence[str], prefix='', **kwargs) -> None:
"""Initializer."""
self._list = sequence
try:
@@ -446,7 +463,7 @@
raise ValueError('The sequence is empty.')
del self._max
- def format(self, default=None):
+ def format(self, default: Optional[str] = None) -> str:
"""Return a string showing the range."""
if not self._list:
raise ValueError('The sequence is empty.')
@@ -458,7 +475,7 @@
"""Return the maximum value."""
return len(self._list)
- def result(self, value):
+ def result(self, value: str) -> Any:
"""Return a tuple with the prefix and selected value."""
return self.prefix, self._list[self.parse(value) - 1]
@@ -472,8 +489,9 @@
before_question = True
- def __init__(self, sequence, prefix='', pre: Optional[str] = None,
- post: Optional[str] = None, **kwargs):
+ def __init__(self, sequence: Sequence[str], prefix: str = '',
+ pre: Optional[str] = None, post: Optional[str] = None,
+ **kwargs) -> None:
"""Initializer.
:param pre: Additional comment printed before the list.
@@ -484,12 +502,12 @@
self.post = post
@property
- def stop(self):
+ def stop(self) -> bool:
"""Return whether this option stops asking."""
return self._stop
@property
- def out(self):
+ def out(self) -> str:
"""Output text of the enumerated list."""
text = ''
if self.pre is not None:
@@ -509,7 +527,7 @@
*New in version 3.0.*
"""
- def test(self, value) -> bool:
+ def test(self, value: str) -> bool:
"""Return whether the values are int and in the specified range."""
try:
values = [self.parse(val) for val in value.split(',')]
@@ -526,7 +544,7 @@
return False
- def result(self, value):
+ def result(self, value: str) -> Any:
"""Return a tuple with the prefix and selected values as a list."""
values = (self.parse(val) for val in value.split(','))
result = [self._list[val - 1] for val in values]
@@ -548,7 +566,7 @@
color = 'lightred'
@property
- def out(self):
+ def out(self) -> str:
"""Highlighted output section of the text."""
start = max(0, self.start - self.context)
end = min(len(self.text), self.end + self.context)
@@ -561,7 +579,7 @@
@deprecated('pywikibot.output(HighlightContextOption.out)',
since='6.2.0')
- def output_range(self, start, end):
+ def output_range(self, start: int, end: int) -> None:
"""DEPRECATED. Show normal context with a highlighted center region."""
color_format = pywikibot.tools.formatter.color_format
text = color_format('{}{%(color)s}{}{default}{}'
@@ -576,7 +594,7 @@
"""The given answer didn't suffice."""
- def __int__(self, stop=False):
+ def __int__(self, stop: bool = False) -> None:
"""Initializer."""
self.stop = stop
@@ -585,7 +603,7 @@
"""A choice for input_choice which result in this exception."""
- def result(self, value):
+ def result(self, value: Any) -> Any:
"""Return itself to raise the exception."""
return self
@@ -594,6 +612,6 @@
"""The user has cancelled processing at a prompt."""
- def __init__(self):
+ def __init__(self) -> None:
"""Constructor using the 'quit' ('q') in input_choice."""
super().__init__('quit', 'q')
To view, visit change 703916. To unsubscribe, or for help writing mail filters, visit settings.