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)
validate_user_option_multi(...) Multi-select version of validate_user_option, returning keys in selection order
validate_user_option_value_multi(...) Multi-select version of validate_user_option_value, returning values in selection order
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
SubstringCompleter (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_multi(...)

validate_user_option_multi(
    input_msg: str = "Option:",
    *args,
    **kwargs: Any  # key -> label
) -> List[Any]
  • Multi-select version of validate_user_option.
  • Auto-adds q: quit unless q=False.
  • Removes already-picked options from the menu.
  • Returns a list of keys in the order they were picked.
STATUS_DICT = {0:"new", 1:"active", 7:"we rejected"}
picked = validate_user_option_multi("Select statuses:", **STATUS_DICT)
# → [1, 7]

validate_user_option_value_multi(...)

validate_user_option_value_multi(
    input_msg: str = "Option:",
    *args,
    **kwargs: Any  # key -> value
) -> List[Any]
  • Multi-select version of validate_user_option_value.
  • Auto-adds q: quit unless q=False.
  • Returns a list of values in the order they were picked.
genres = validate_user_option_value_multi("Pick genres:", a="Action", c="Comedy", d="Drama")
# User picks: c, a → returns ['Comedy', 'Action']

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.3.tar.gz (18.9 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.3-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: askuser-0.1.3.tar.gz
  • Upload date:
  • Size: 18.9 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.3.tar.gz
Algorithm Hash digest
SHA256 e51b10f9f43af410de39889f3c3610970deee01860555d85110d8d01df6682eb
MD5 35a4cbb3c659fbc66791b2d7e650507f
BLAKE2b-256 089f4779b8a51700286e9d89ec0c2013799bfbc5110120efe11f7b1da4a62af0

See more details on using hashes here.

File details

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

File metadata

  • Download URL: askuser-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 14.6 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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 c404e0ba3c433e2d00c4817610d936d3ea811d9bcaf002895ac90abd14353bbe
MD5 bef372ef84bcc7dbd86ae10bbe76dc12
BLAKE2b-256 644fa44a505a17e67ec42801f3612b379a1f3c663bbe9656dabaf54e52a62d27

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