jenkins-bot submitted this change.

View Change

Approvals: JJMC89: Looks good to me, approved jenkins-bot: Verified
[bugfix] Improvements for input_list_choice()

- add doc and typing hints to UI.input_list_choice().
- simplify line_template for printing answers Sequence.
- do not print answers Collection if force option is set.
The default value is already shown.
- Test input result for TypeError which may occur if default is None.
- raise ValueError if force is set and default value is not set
or default value is invalid; otherwise it would cause an infinite
loop.
- early return the answer value if it is member of answers Sequence.
- default value of input_list_choice may be an int too
- add Sequence to backports.py for typing hints

Bug: T272237
Change-Id: Icb9ac82264c15d13e0dcff82d63cff71163c5a5b
---
M pywikibot/backports.py
M pywikibot/bot.py
M pywikibot/userinterfaces/terminal_interface_base.py
3 files changed, 39 insertions(+), 28 deletions(-)

diff --git a/pywikibot/backports.py b/pywikibot/backports.py
index afe8383..487447a 100644
--- a/pywikibot/backports.py
+++ b/pywikibot/backports.py
@@ -1,6 +1,6 @@
"""This module contains backports to support older Python versions."""
#
-# (C) Pywikibot team, 2020
+# (C) Pywikibot team, 2020-2021
#
# Distributed under the terms of the MIT license.
#
@@ -23,14 +23,14 @@
from collections import defaultdict as DefaultDict # noqa: N812

if PYTHON_VERSION >= (3, 9):
- from collections.abc import Iterable
+ from collections.abc import Iterable, Sequence
Dict = dict
FrozenSet = frozenset
List = list
Set = set
Tuple = tuple
else:
- from typing import Dict, FrozenSet, Iterable, List, Set, Tuple
+ from typing import Dict, FrozenSet, Iterable, List, Set, Sequence, Tuple


# PEP 616 string methods
diff --git a/pywikibot/bot.py b/pywikibot/bot.py
index 204ed9b..7f514cf 100644
--- a/pywikibot/bot.py
+++ b/pywikibot/bot.py
@@ -102,7 +102,7 @@
from warnings import warn

import pywikibot
-from pywikibot.backports import Dict, Iterable, List
+from pywikibot.backports import Dict, Iterable, List, Sequence
from pywikibot import config2 as config
from pywikibot import daemonize
from pywikibot import i18n
@@ -510,15 +510,14 @@
automatic_quit=automatic_quit, force=force) == 'y'


-def input_list_choice(question: str, answers,
- default: Optional[str] = None,
+def input_list_choice(question: str, answers: Sequence[Any],
+ default: Union[int, str, None] = None,
force: bool = False) -> str:
"""
Ask the user the question and return one of the valid answers.

@param question: The question asked without trailing spaces.
@param answers: The valid answers each containing a full length answer.
- @type answers: Iterable of str
@param default: The result if no answer was entered. It must not be in the
valid answers and can be disabled by setting it to None.
@param force: Automatically use the default
diff --git a/pywikibot/userinterfaces/terminal_interface_base.py b/pywikibot/userinterfaces/terminal_interface_base.py
index bc314f6..2ca2ec8 100755
--- a/pywikibot/userinterfaces/terminal_interface_base.py
+++ b/pywikibot/userinterfaces/terminal_interface_base.py
@@ -1,21 +1,22 @@
# -*- coding: utf-8 -*-
"""Base for terminal user interfaces."""
#
-# (C) Pywikibot team, 2003-2020
+# (C) Pywikibot team, 2003-2021
#
# Distributed under the terms of the MIT license.
#
import getpass
import logging
-import math
import re
import sys
import threading

-from typing import Optional
+from typing import Any, Optional, Union

import pywikibot
from pywikibot import config2 as config
+
+from pywikibot.backports import Sequence
from pywikibot.bot import VERBOSE, INFO, STDOUT, INPUT, WARNING
from pywikibot.bot_choice import (ChoiceException, Option, OutputOption,
QuitKeyboardInterrupt, StandardOption)
@@ -380,31 +381,42 @@
return index
return answer

- def input_list_choice(self, question, answers, default=None, force=False):
- """Ask the user to select one entry from a list of entries."""
- message = question
- clist = answers
+ def input_list_choice(self, question: str, answers: Sequence[Any],
+ default: Union[int, str, None] = None,
+ force: bool = False) -> Any:
+ """Ask the user to select one entry from a list of entries.

- line_template = '{{0: >{0}}}: {{1}}'.format(
- int(math.log10(len(clist)) + 1))
- for n, i in enumerate(clist):
- pywikibot.output(line_template.format(n + 1, i))
+ @param question: The question, without trailing whitespace.
+ @param answers: A sequence of options to be choosen.
+ @param default: The default answer if no was entered. None to require
+ an answer.
+ @param force: Automatically use the default.
+ @return: Return a single Sequence entry.
+ """
+ if not force:
+ line_template = '{{0: >{0}}}: {{1}}'.format(len(str(len(answers))))
+ for i, entry in enumerate(answers, start=1):
+ pywikibot.output(line_template.format(i, entry))

while True:
- choice = self.input(message, default=default, force=force)
+ choice = self.input(question, default=default, force=force)
+
try:
choice = int(choice) - 1
- except ValueError:
- try:
- choice = clist.index(choice)
- except IndexError:
- choice = -1
+ except (TypeError, ValueError):
+ if choice in answers:
+ return choice
+ choice = -1

# User typed choice number
- if 0 <= choice < len(clist):
- return clist[choice]
- else:
- pywikibot.error('Invalid response')
+ if 0 <= choice < len(answers):
+ return answers[choice]
+
+ if force:
+ raise ValueError('Invalid value "{}" for default during force.'
+ .format(default))
+
+ pywikibot.error('Invalid response')

def editText(self, text: str, jumpIndex: Optional[int] = None,
highlight: Optional[str] = None):

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

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: Icb9ac82264c15d13e0dcff82d63cff71163c5a5b
Gerrit-Change-Number: 656608
Gerrit-PatchSet: 7
Gerrit-Owner: Xqt <info@gno.de>
Gerrit-Reviewer: JJMC89 <JJMC89.Wikimedia@gmail.com>
Gerrit-Reviewer: Mpaa <mpaa.wiki@gmail.com>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged