Skip to content

Commit

Permalink
add questionary module from 'tmbo/questionary/pull/330'
Browse files Browse the repository at this point in the history
  • Loading branch information
weiduhuo committed Oct 29, 2023
1 parent ffc3502 commit 040e9d5
Show file tree
Hide file tree
Showing 23 changed files with 2,914 additions and 0 deletions.
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ pluggy
httpcore
pydantic
jsonschema
prompt-toolkit<=3.0.36,>=2.0
51 changes: 51 additions & 0 deletions utils/questionary/NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Tom Bocklisch
Copyright 2019 Tom Bocklisch

----

This product includes software from PyInquirer (https://github.com/CITGuru/PyInquirer),
under the MIT License.

Copyright 2018 Oyetoke Toby and contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

----

This product includes software from whaaaaat (https://github.com/finklabs/whaaaaat),
under the MIT License.

Copyright 2016 Fink Labs GmbH and inquirerpy contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
114 changes: 114 additions & 0 deletions utils/questionary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Questionary

[![Version](https://img.shields.io/pypi/v/questionary.svg)](https://pypi.org/project/questionary/)
[![License](https://img.shields.io/pypi/l/questionary.svg)](#)
[![Continuous Integration](https://github.com/tmbo/questionary/workflows/Continuous%20Integration/badge.svg)](#)
[![Coverage](https://coveralls.io/repos/github/tmbo/questionary/badge.svg?branch=master)](https://coveralls.io/github/tmbo/questionary?branch=master)
[![Supported Python Versions](https://img.shields.io/pypi/pyversions/questionary.svg)](https://pypi.python.org/pypi/questionary)
[![Documentation](https://readthedocs.org/projects/questionary/badge/?version=latest)](https://questionary.readthedocs.io/en/latest/?badge=latest)

✨ Questionary is a Python library for effortlessly building pretty command line interfaces ✨

* [Features](#features)
* [Installation](#installation)
* [Usage](#usage)
* [Documentation](#documentation)
* [Support](#support)


![Example](https://raw.githubusercontent.com/tmbo/questionary/master/docs/images/example.gif)

```python3
import questionary

questionary.text("What's your first name").ask()
questionary.password("What's your secret?").ask()
questionary.confirm("Are you amazed?").ask()

questionary.select(
"What do you want to do?",
choices=["Order a pizza", "Make a reservation", "Ask for opening hours"],
).ask()

questionary.rawselect(
"What do you want to do?",
choices=["Order a pizza", "Make a reservation", "Ask for opening hours"],
).ask()

questionary.checkbox(
"Select toppings", choices=["foo", "bar", "bazz"]
).ask()

questionary.path("Path to the projects version file").ask()
```

Used and supported by

[<img src="https://raw.githubusercontent.com/tmbo/questionary/master/docs/images/rasa-logo.svg" width="200">](https://github.com/RasaHQ/rasa)

## Features

Questionary supports the following input prompts:

* [Text](https://questionary.readthedocs.io/en/stable/pages/types.html#text)
* [Password](https://questionary.readthedocs.io/en/stable/pages/types.html#password)
* [File Path](https://questionary.readthedocs.io/en/stable/pages/types.html#file-path)
* [Confirmation](https://questionary.readthedocs.io/en/stable/pages/types.html#confirmation)
* [Select](https://questionary.readthedocs.io/en/stable/pages/types.html#select)
* [Raw select](https://questionary.readthedocs.io/en/stable/pages/types.html#raw-select)
* [Checkbox](https://questionary.readthedocs.io/en/stable/pages/types.html#checkbox)
* [Autocomplete](https://questionary.readthedocs.io/en/stable/pages/types.html#autocomplete)

There is also a helper to [print formatted text](https://questionary.readthedocs.io/en/stable/pages/types.html#printing-formatted-text)
for when you want to spice up your printed messages a bit.

## Installation

Use the package manager [pip](https://pip.pypa.io/en/stable/) to install Questionary:

```bash
$ pip install questionary
✨🎂✨
```

## Usage

```python
import questionary

questionary.select(
"What do you want to do?",
choices=[
'Order a pizza',
'Make a reservation',
'Ask for opening hours'
]).ask() # returns value of selection
```

That's all it takes to create a prompt! Have a [look at the documentation](https://questionary.readthedocs.io/)
for some more examples.

## Documentation

Documentation for Questionary is available [here](https://questionary.readthedocs.io/).

## Support

Please [open an issue](https://github.com/tmbo/questionary/issues/new)
with enough information for us to reproduce your problem.
A [minimal, reproducible example](https://stackoverflow.com/help/minimal-reproducible-example)
would be very helpful.

## Contributing

Contributions are very much welcomed and appreciated. Head over to the documentation on [how to contribute](https://questionary.readthedocs.io/en/stable/pages/contributors.html#steps-for-submitting-code).

## Authors and Acknowledgment

Questionary is written and maintained by Tom Bocklisch and Kian Cross.

It is based on the great work by [Oyetoke Toby](https://github.com/CITGuru/PyInquirer)
and [Mark Fink](https://github.com/finklabs/whaaaaat).

## License
Licensed under the [MIT License](https://github.com/tmbo/questionary/blob/master/LICENSE). Copyright 2021 Tom Bocklisch.
54 changes: 54 additions & 0 deletions utils/questionary/questionary/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# noinspection PyUnresolvedReferences
from prompt_toolkit.styles import Style
from prompt_toolkit.validation import ValidationError
from prompt_toolkit.validation import Validator

from .version import __version__
from .form import Form
from .form import FormField
from .form import form
from .prompt import prompt
from .prompt import unsafe_prompt

# import the shortcuts to create single question prompts
from .prompts.autocomplete import autocomplete
from .prompts.checkbox import checkbox
from .prompts.common import Choice
from .prompts.common import Separator
from .prompts.common import print_formatted_text as print
from .prompts.confirm import confirm
from .prompts.password import password
from .prompts.path import path
from .prompts.press_any_key_to_continue import press_any_key_to_continue
from .prompts.rawselect import rawselect
from .prompts.select import select
from .prompts.text import text
from .question import Question

__all__ = [
"__version__",
# question types
"autocomplete",
"checkbox",
"confirm",
"password",
"path",
"press_any_key_to_continue",
"rawselect",
"select",
"text",
# utility methods
"print",
"form",
"prompt",
"unsafe_prompt",
# commonly used classes
"Form",
"FormField",
"Question",
"Choice",
"Style",
"Separator",
"Validator",
"ValidationError",
]
49 changes: 49 additions & 0 deletions utils/questionary/questionary/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from . import Style

# Value to display as an answer when "affirming" a confirmation question
YES = "Yes"

# Value to display as an answer when "denying" a confirmation question
NO = "No"

# Instruction text for a confirmation question (yes is default)
YES_OR_NO = "(Y/n)"

# Instruction text for a confirmation question (no is default)
NO_OR_YES = "(y/N)"

# Instruction for multiline input
INSTRUCTION_MULTILINE = "(Finish with 'Alt+Enter' or 'Esc then Enter')\n>"

# Selection token used to indicate the selection cursor in a list
DEFAULT_SELECTED_POINTER = "»"

# Item prefix to identify selected items in a checkbox list
INDICATOR_SELECTED = "●"

# Item prefix to identify unselected items in a checkbox list
INDICATOR_UNSELECTED = "○"

# Prefix displayed in front of questions
DEFAULT_QUESTION_PREFIX = "?"

# Message shown when a user aborts a question prompt using CTRL-C
DEFAULT_KBI_MESSAGE = "Cancelled by user"

# Default text shown when the input is invalid
INVALID_INPUT = "Invalid input"

# Default message style
DEFAULT_STYLE = Style(
[
("qmark", "fg:#5f819d"), # token in front of the question
("question", "bold"), # question text
("answer", "fg:#FF9D00 bold"), # submitted answer text behind the question
("pointer", ""), # pointer used in select and checkbox prompts
("selected", ""), # style for a selected item of a checkbox
("separator", ""), # separator in lists
("instruction", ""), # user instructions for select, rawselect, checkbox
("text", ""), # any other text
("instruction", ""), # user instructions for select, rawselect, checkbox
]
)
122 changes: 122 additions & 0 deletions utils/questionary/questionary/form.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@

from typing import Any
from typing import Dict
from typing import NamedTuple
from typing import Sequence

from .constants import DEFAULT_KBI_MESSAGE
from .question import Question


class FormField(NamedTuple):
"""
Represents a question within a form
Args:
key: The name of the form field.
question: The question to ask in the form field.
"""

key: str
question: Question


def form(**kwargs: Question) -> "Form":
"""Create a form with multiple questions.
The parameter name of a question will be the key for the answer in
the returned dict.
Args:
kwargs: Questions to ask in the form.
"""
return Form(*(FormField(k, q) for k, q in kwargs.items()))


class Form:
"""Multi question prompts. Questions are asked one after another.
All the answers are returned as a dict with one entry per question.
This class should not be invoked directly, instead use :func:`form`.
"""

form_fields: Sequence[FormField]

def __init__(self, *form_fields: FormField) -> None:
self.form_fields = form_fields

def unsafe_ask(self, patch_stdout: bool = False) -> Dict[str, Any]:
"""Ask the questions synchronously and return user response.
Does not catch keyboard interrupts.
Args:
patch_stdout: Ensure that the prompt renders correctly if other threads
are printing to stdout.
Returns:
The answers from the form.
"""
return {f.key: f.question.unsafe_ask(patch_stdout) for f in self.form_fields}

async def unsafe_ask_async(self, patch_stdout: bool = False) -> Dict[str, Any]:
"""Ask the questions using asyncio and return user response.
Does not catch keyboard interrupts.
Args:
patch_stdout: Ensure that the prompt renders correctly if other threads
are printing to stdout.
Returns:
The answers from the form.
"""
return {
f.key: await f.question.unsafe_ask_async(patch_stdout)
for f in self.form_fields
}

def ask(
self, patch_stdout: bool = False, kbi_msg: str = DEFAULT_KBI_MESSAGE
) -> Dict[str, Any]:
"""Ask the questions synchronously and return user response.
Args:
patch_stdout: Ensure that the prompt renders correctly if other threads
are printing to stdout.
kbi_msg: The message to be printed on a keyboard interrupt.
Returns:
The answers from the form.
"""
try:
return self.unsafe_ask(patch_stdout)
except KeyboardInterrupt:
print("")
print(kbi_msg)
print("")
return {}

async def ask_async(
self, patch_stdout: bool = False, kbi_msg: str = DEFAULT_KBI_MESSAGE
) -> Dict[str, Any]:
"""Ask the questions using asyncio and return user response.
Args:
patch_stdout: Ensure that the prompt renders correctly if other threads
are printing to stdout.
kbi_msg: The message to be printed on a keyboard interrupt.
Returns:
The answers from the form.
"""
try:
return await self.unsafe_ask_async(patch_stdout)
except KeyboardInterrupt:
print("")
print(kbi_msg)
print("")
return {}
Loading

0 comments on commit 040e9d5

Please sign in to comment.