Skip to main content

Smart and robust CLI input: validated prompts, menu-driven selections, default value support, and smart autocompletion.

Project description

AskUser

AskUser is a smart CLI utility for collecting and validating user input in Python. It wraps common prompt patterns—validation, menus, defaults, and autocompletion—into a simple, consistent API.


📦 Installation

pip install askuser

📖 API Overview

Function / Class What It Does
validate_input(...) Prompt for free-form input, validate type/pattern, show hints (min/max/default) and retry until valid
pretty_menu(*args, **kwargs) Print a formatted menu of positional (0:) and keyword (x:) options
validate_user_option(...) Show a menu, auto-add q: quit (unless q=False), prompt user, and return the selected key
validate_user_option_value(...) Like validate_user_option, but maps the chosen key to its value
validate_user_option_enumerated(dict,...) Enumerate a dict into numbered options, add q: quit, and return (key, value)
choose_from_db(list_of_dicts,...) Tabulate DB rows, prompt for an existing id, optionally accept xq: quit, and return (id, row_dict)
choose_dict_from_list_of_dicts(list, field) Display each item’s field as a menu, return the selected dict
yes(prompt, default=None) Shorthand for validate_input(prompt, "yes_no", default) == "y" → returns bool
SpeechCompleter (internal) a prompt_toolkit completer that finds substring matches in suggestions
user_prompt(prompt, items, return_value=False) Prompt with autocomplete over a list/dict of items; if return_value=True (and items is a dict), returns the value instead.

🔍 validate_input

validate_input(
    input_msg: str,
    validation_type: Literal[
      'custom','required','not_in','none_if_blank','yes_no',
      'int','float','alpha','alphanum','custom_chars','regex',
      'date','future_date','time','url','ms_url','slug','email','phone',
      'currency','country','language','movie_ids','ss_video_ids','bundle_ids'
    ],
    expected_inputs: list = None,
    not_in:        list = None,
    maximum:       Union[int, float] = None,
    minimum:       Union[int, float] = None,
    allowed_chars: str = None,
    allowed_regex: str = None,
    default:       Any = None
) -> Union[str,int,float,None]
  • Default values
    • If you set default, pressing Enter returns the default.
  • Hints added automatically
    • yes_no adds (y/n)
    • none_if_blank adds (optional)
    • time adds (hh:mm:ss)
    • maximum/minimum display (max: …) / (min: …)
    • default displays (default: …)
  • Validation types
    • Built-in: int, float, alpha, alphanum, date, future_date, time, url, email, phone, slug, etc.
    • Custom:
      • custom with expected_inputs=[…]
      • not_in with not_in=[…]
      • custom_chars with allowed_chars="abc123"
      • regex with allowed_regex="^[A-Z]+$"

Example:

# integer with bounds & default
count = validate_input(
    "How many items?", "int",
    minimum=1, maximum=100,
    default=10
)
# • Blank → count == 10  
# • Invalid/out-of-range → re-prompt

🧭 Menus & Options

pretty_menu(*args, **kwargs)

Prints a menu—no prompt:

pretty_menu("List", "Add", d="Delete", q="Quit")
# 0: List    1: Add    d: Delete    q: Quit

validate_user_option(...)

validate_user_option(
    input_msg: str = "Option:",
    *args: str,
    **kwargs: str|bool  # pass q=False to suppress quit
) -> str
  • Auto-adds q: quit unless q=False.
  • Returns the chosen key.

Examples:

opt = validate_user_option("Pick:", "Red","Blue", g="Green")
# keys: '0','1','g','q'

opt = validate_user_option("Pick:", "One","Two", q=False)
# keys: '0','1'

validate_user_option_value(...)

validate_user_option_value(
    input_msg: str = "Option:",
    *args,
    **kwargs: Any  # key -> value
) -> Any
  • Builds same menu, returns the value.
genre = validate_user_option_value(a="Action", c="Comedy", d="Drama")
# 'c' → "Comedy"

validate_user_option_enumerated(dict, msg="Option:", start=1)

validate_user_option_enumerated(
    a_dict: Dict[Any, str],
    msg: str = "Option:",
    start: int = 1
) -> Tuple[Any, str]
  • Enumerates .items() starting at start.
  • Adds q: quit.
  • Returns (key, value) or ('q', None).
movies = {101:"Inception", 202:"Memento"}
mid, title = validate_user_option_enumerated(movies)

🗄 Database-Style Selection

choose_from_db(db_result, input_msg=None, table_desc=None, xq=False)

choose_from_db(
    db_result: List[Dict[str,Any]],
    input_msg: str = None,
    table_desc: str = None,
    xq: bool = False
) -> Tuple[int, Dict]
  • Pretty-prints rows with tabulate.
  • Only existing id values in db_result are valid.
  • If xq=True, also accepts xq → returns ('xq','quit').
  • Invalid entries re-prompt.

choose_dict_from_list_of_dicts(list_of_dicts, key_to_choose)

choose_dict_from_list_of_dicts(
    list_of_dicts: List[Dict],
    key_to_choose: str
) -> Dict
  • Menu of dict[key_to_choose].
  • Returns selected dict.
fruits = [{"name":"Apple"},{"name":"Banana"}]
choice = choose_dict_from_list_of_dicts(fruits, "name")

✅ Yes/No Shortcut

yes("Continue?", default="y")  # True if 'y', False if 'n'; blank → default

💬 Autocomplete

from askuser.autocomplete import user_prompt

res = user_prompt("Country: ", ["USA","UK","IN"], return_value=False)
# ≥2 chars → suggestions

opts = {"us":"United States","uk":"United Kingdom"}
code = user_prompt("Code: ", opts, return_value=True)
# returns 'us'

🔍 All Validation Types

Type Description
int / float Numeric with optional minimum / maximum
alpha / alphanum Only letters / letters+digits
date / future_date YYYY-MM-DD or YYYY-MM-DD HH:MM:SS
time HH:MM:SS
email Standard RFC email
phone Digits with optional +, strips spaces/dashes
url Hostname + optional path/query
slug [a-z0-9-] only, single delimiter
custom Only values in expected_inputs
not_in Reject values in not_in
custom_chars Only chars in allowed_chars
regex Match your regex
language ISO-639 via pycountry
country Recognizes common abbreviations for countries

🧪 Testing

Under tests/, examples:

import pytest
from askuser import validate_input, yes, validate_user_option

def test_default(monkeypatch):
    monkeypatch.setattr('builtins.input', lambda _: '')
    assert validate_input("Prompt?", "int", default=7) == 7

def test_no_quit(monkeypatch):
    monkeypatch.setattr('builtins.input', lambda _: '0')
    assert validate_user_option("Pick:", "A","B", q=False) == '0'

Run:

pytest tests/

📜 License

MIT — free to use, modify, and distribute.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

askuser-0.1.0.tar.gz (18.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

askuser-0.1.0-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file askuser-0.1.0.tar.gz.

File metadata

  • Download URL: askuser-0.1.0.tar.gz
  • Upload date:
  • Size: 18.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.0

File hashes

Hashes for askuser-0.1.0.tar.gz
Algorithm Hash digest
SHA256 93fac7e05e71e0fff585d1d1c79776bd95f494248ace7e2bb1bea76940147be7
MD5 b97e2d1d9fc5b815ee823422f2344fba
BLAKE2b-256 d6f825ffe21d2f8367c83aa3719cb01055c2e57905a1e9d6da8281dcb9d5794a

See more details on using hashes here.

File details

Details for the file askuser-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: askuser-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.0

File hashes

Hashes for askuser-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 94eb68621fcd474a17e7e619d5fc8c573a85b4884b7cea6ae26e33b4862de899
MD5 032c78a8b9013c080000d042c75f3426
BLAKE2b-256 3abae40dc9fb50336f8823e90996eb6e1151d51d592e268a9acf5ae1bd443718

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page