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.1.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.1-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: askuser-0.1.1.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.1.tar.gz
Algorithm Hash digest
SHA256 a5866018f675a819f120e17fbed1ac38e7ccfafba0e22720ce3d368a2913253a
MD5 24f63afa6fd7b4c0efc523a44ee76c2b
BLAKE2b-256 a31dfa8aac678c1f0fe36547e18128c25527b3e81c5fbc675a2ef918fc319164

See more details on using hashes here.

File details

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

File metadata

  • Download URL: askuser-0.1.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 823ebf08c9e13d8f5fb17ee13e8ee3db5a96c945e46b344d007fb1f2480a7f2f
MD5 2ae7ca1e8f5ca0126d22536c92d588f5
BLAKE2b-256 f29e172d2f8cc139cd353cc5e13d90fa0dfd1848d87f08db6195e594b1a86c39

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