zhzhang93's picture
Upload folder using huggingface_hub
113ea10 verified
from __future__ import annotations
from typing import Generic, Sequence, TypeVar
from prompt_toolkit.application import Application
from prompt_toolkit.filters import (
Condition,
FilterOrBool,
is_done,
renderer_height_is_known,
to_filter,
)
from prompt_toolkit.formatted_text import AnyFormattedText
from prompt_toolkit.key_binding.key_bindings import (
DynamicKeyBindings,
KeyBindings,
KeyBindingsBase,
merge_key_bindings,
)
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
from prompt_toolkit.layout import (
AnyContainer,
ConditionalContainer,
HSplit,
Layout,
Window,
)
from prompt_toolkit.layout.controls import FormattedTextControl
from prompt_toolkit.layout.dimension import Dimension
from prompt_toolkit.styles import BaseStyle, Style
from prompt_toolkit.utils import suspend_to_background_supported
from prompt_toolkit.widgets import Box, Frame, Label, RadioList
__all__ = [
"ChoiceInput",
"choice",
]
_T = TypeVar("_T")
E = KeyPressEvent
def create_default_choice_input_style() -> BaseStyle:
return Style.from_dict(
{
"frame.border": "#884444",
"selected-option": "bold",
}
)
class ChoiceInput(Generic[_T]):
"""
Input selection prompt. Ask the user to choose among a set of options.
Example usage::
input_selection = ChoiceInput(
message="Please select a dish:",
options=[
("pizza", "Pizza with mushrooms"),
("salad", "Salad with tomatoes"),
("sushi", "Sushi"),
],
default="pizza",
)
result = input_selection.prompt()
:param message: Plain text or formatted text to be shown before the options.
:param options: Sequence of ``(value, label)`` tuples. The labels can be
formatted text.
:param default: Default value. If none is given, the first option is
considered the default.
:param mouse_support: Enable mouse support.
:param style: :class:`.Style` instance for the color scheme.
:param symbol: Symbol to be displayed in front of the selected choice.
:param bottom_toolbar: Formatted text or callable that returns formatted
text to be displayed at the bottom of the screen.
:param show_frame: `bool` or
:class:`~prompt_toolkit.filters.Filter`. When True, surround the input
with a frame.
:param enable_interrupt: `bool` or
:class:`~prompt_toolkit.filters.Filter`. When True, raise
the ``interrupt_exception`` (``KeyboardInterrupt`` by default) when
control-c has been pressed.
:param interrupt_exception: The exception type that will be raised when
there is a keyboard interrupt (control-c keypress).
"""
def __init__(
self,
*,
message: AnyFormattedText,
options: Sequence[tuple[_T, AnyFormattedText]],
default: _T | None = None,
mouse_support: bool = False,
style: BaseStyle | None = None,
symbol: str = ">",
bottom_toolbar: AnyFormattedText = None,
show_frame: FilterOrBool = False,
enable_suspend: FilterOrBool = False,
enable_interrupt: FilterOrBool = True,
interrupt_exception: type[BaseException] = KeyboardInterrupt,
key_bindings: KeyBindingsBase | None = None,
) -> None:
if style is None:
style = create_default_choice_input_style()
self.message = message
self.default = default
self.options = options
self.mouse_support = mouse_support
self.style = style
self.symbol = symbol
self.show_frame = show_frame
self.enable_suspend = enable_suspend
self.interrupt_exception = interrupt_exception
self.enable_interrupt = enable_interrupt
self.bottom_toolbar = bottom_toolbar
self.key_bindings = key_bindings
def _create_application(self) -> Application[_T]:
radio_list = RadioList(
values=self.options,
default=self.default,
select_on_focus=True,
open_character="",
select_character=self.symbol,
close_character="",
show_cursor=False,
show_numbers=True,
container_style="class:input-selection",
default_style="class:option",
selected_style="",
checked_style="class:selected-option",
number_style="class:number",
show_scrollbar=False,
)
container: AnyContainer = HSplit(
[
Box(
Label(text=self.message, dont_extend_height=True),
padding_top=0,
padding_left=1,
padding_right=1,
padding_bottom=0,
),
Box(
radio_list,
padding_top=0,
padding_left=3,
padding_right=1,
padding_bottom=0,
),
]
)
@Condition
def show_frame_filter() -> bool:
return to_filter(self.show_frame)()
show_bottom_toolbar = (
Condition(lambda: self.bottom_toolbar is not None)
& ~is_done
& renderer_height_is_known
)
container = ConditionalContainer(
Frame(container),
alternative_content=container,
filter=show_frame_filter,
)
bottom_toolbar = ConditionalContainer(
Window(
FormattedTextControl(
lambda: self.bottom_toolbar, style="class:bottom-toolbar.text"
),
style="class:bottom-toolbar",
dont_extend_height=True,
height=Dimension(min=1),
),
filter=show_bottom_toolbar,
)
layout = Layout(
HSplit(
[
container,
# Add an empty window between the selection input and the
# bottom toolbar, if the bottom toolbar is visible, in
# order to allow the bottom toolbar to be displayed at the
# bottom of the screen.
ConditionalContainer(Window(), filter=show_bottom_toolbar),
bottom_toolbar,
]
),
focused_element=radio_list,
)
kb = KeyBindings()
@kb.add("enter", eager=True)
def _accept_input(event: E) -> None:
"Accept input when enter has been pressed."
event.app.exit(result=radio_list.current_value, style="class:accepted")
@Condition
def enable_interrupt() -> bool:
return to_filter(self.enable_interrupt)()
@kb.add("c-c", filter=enable_interrupt)
@kb.add("<sigint>", filter=enable_interrupt)
def _keyboard_interrupt(event: E) -> None:
"Abort when Control-C has been pressed."
event.app.exit(exception=self.interrupt_exception(), style="class:aborting")
suspend_supported = Condition(suspend_to_background_supported)
@Condition
def enable_suspend() -> bool:
return to_filter(self.enable_suspend)()
@kb.add("c-z", filter=suspend_supported & enable_suspend)
def _suspend(event: E) -> None:
"""
Suspend process to background.
"""
event.app.suspend_to_background()
return Application(
layout=layout,
full_screen=False,
mouse_support=self.mouse_support,
key_bindings=merge_key_bindings(
[kb, DynamicKeyBindings(lambda: self.key_bindings)]
),
style=self.style,
)
def prompt(self) -> _T:
return self._create_application().run()
async def prompt_async(self) -> _T:
return await self._create_application().run_async()
def choice(
message: AnyFormattedText,
*,
options: Sequence[tuple[_T, AnyFormattedText]],
default: _T | None = None,
mouse_support: bool = False,
style: BaseStyle | None = None,
symbol: str = ">",
bottom_toolbar: AnyFormattedText = None,
show_frame: bool = False,
enable_suspend: FilterOrBool = False,
enable_interrupt: FilterOrBool = True,
interrupt_exception: type[BaseException] = KeyboardInterrupt,
key_bindings: KeyBindingsBase | None = None,
) -> _T:
"""
Choice selection prompt. Ask the user to choose among a set of options.
Example usage::
result = choice(
message="Please select a dish:",
options=[
("pizza", "Pizza with mushrooms"),
("salad", "Salad with tomatoes"),
("sushi", "Sushi"),
],
default="pizza",
)
:param message: Plain text or formatted text to be shown before the options.
:param options: Sequence of ``(value, label)`` tuples. The labels can be
formatted text.
:param default: Default value. If none is given, the first option is
considered the default.
:param mouse_support: Enable mouse support.
:param style: :class:`.Style` instance for the color scheme.
:param symbol: Symbol to be displayed in front of the selected choice.
:param bottom_toolbar: Formatted text or callable that returns formatted
text to be displayed at the bottom of the screen.
:param show_frame: `bool` or
:class:`~prompt_toolkit.filters.Filter`. When True, surround the input
with a frame.
:param enable_interrupt: `bool` or
:class:`~prompt_toolkit.filters.Filter`. When True, raise
the ``interrupt_exception`` (``KeyboardInterrupt`` by default) when
control-c has been pressed.
:param interrupt_exception: The exception type that will be raised when
there is a keyboard interrupt (control-c keypress).
"""
return ChoiceInput[_T](
message=message,
options=options,
default=default,
mouse_support=mouse_support,
style=style,
symbol=symbol,
bottom_toolbar=bottom_toolbar,
show_frame=show_frame,
enable_suspend=enable_suspend,
enable_interrupt=enable_interrupt,
interrupt_exception=interrupt_exception,
key_bindings=key_bindings,
).prompt()