Package: pyask
Table of ContentsTable of ContentspyaskBrief OverviewInstallationUseask()process()Convenience functionsNumber convenience functions in pyask.numericdecimal()integer()number()number_between()number_eq_to()number_greater_than()number_greater_than_or_eq()number_less_than()number_less_than_or_eq()seconds()Filepath convenience functions in pyask.pathdirname()filename()pathname()Selection convenience functions in pyask.selectionwhich()which_items()String convenience functions in pyask.stringany_string()char()string_regex()yes_no()pyaskA python library for terminal programs to ask users questions
and validate their responses.
It is pure Python (3.6+)
and requires no dependencies beyond the standard library.Brief OverviewDo you wish you could easily ask a user a question
and keep on asking it until they give a valid response?
Now you can! Here's an example of asking the user for a
time duration in seconds:>>> answer = pyask.seconds("How long is the game?")
How long is the game? (enter time in HH:MM:SS, MM:SS, or SS format) [] -10
Invalid respose.
How long is the game? (enter time in HH:MM:SS, MM:SS, or SS format) [] 10:61
Invalid respose.
How long is the game? (enter time in HH:MM:SS, MM:SS, or SS format) [] 10:59
>>> answer
659
>>> type(answer)
<class 'int'>
>Note that it checked to make sure the answer was a valid timestamp
and continued to ask the user until they gave a valid response.
It then returned an integer so you can immediately put their response to use in your program.Here's another example, this time asking a user to select the filenames for videos to concatenate together.
Note that videos are not named nicely, so sorting them alphabetically and putting them together would scramble the video!
Also note that there are non-video files you would not want to put together in the directory:>>> pyask.which_items(os.listdir(), "What videos, in order, should be stitched together?")
Available choices are:
(0) video1.mp4
(1) video3.mp4
(2) usb.txt
(3) video2.mp4
(4) video_4.mp4
What videos, in order, should be stitched together? (space/comma separated number(s), repeats ok) [] 0 3 1 4
['video1.mp4', 'video2.mp4', 'video3.mp4', 'video_4.mp4']It returns a nice list in the order the user specified.
They can also use commas and any variable number of spaces to separate items.The package can ask many types of questions about numbers,
strings, selecting multiple items, and filenames.
Please see below for more information.Installationpyaskis available on the Python Package Index (PyPI) and
can be installed withpip:$ pip install pyaskUseask()pyask's main functionality stems from the functionask():ask(question, aid="", default="", allow_empty=False, process_func=whatever, choices=None)ask()takes the following arguments:question: a question in the form of a string.
Including a question mark is optional, so `"What is your favorite color?" and
"What is your favorite color" are equivalent.aid: Optional and defaults to an empty string.
Provides user with aid to answer the question. So settingaid="primary color"to the above question will ask the userWhat is your favorite color? (primary color).default: Optional and defaults to an empty string. Whatever you
provide will be the assumed answer from the user if they simply hitEnterat the prompt rather than providing an answer.
Providing a default response will prevent the user from entering a blank response.
Providing a default of "blue" to the above question would prompt:What is your favorite color? (primary color) [blue]allow_empty: Optional and defaults toFalse.
Setting it toTruewill prevent the user from entering a blank response
(remember, if you provide adefaultthen the user will not be able to enter a blank response).process_func: Defaults towhatever,
a function that keeps the user's answer and returns it as is.
It is easy to build an appropriateprocess_funcusingpyask.utils.process().
See documentation below for full explanation, but in brief,
aprocess_funcwill first process the user input into a certain form
(be it a number, list of characters, etc) then
validate that it meets the needed requirements (e.g., is positive for example).choices: Optional and defaults toNone.
Will provide the user with a list of choices to select from the sequence provided.
Options are numbered with the index (0-based) of the sequence.ask()then returns the user's answer. If the user's answer is not a valid one,
it will inform the user it is not valid then redo the prompt.
This can continue indefinitely until the user provides a valid response.Empty responses returnNone.process()process(func, valid, s)Takesfuncandvalidfunctions with a string,s,
and returnssprocessed byfuncifvalid(func(s))isTrue.
Otherwise, it raises aValueError. Bothfuncandvalidare functions.
Best used withpartialfrom Python's standard libraryfunctools, like:partial(process, int, lambda x: x > 0)Which would create a function that takes a string,
casts it as an integer, then returns it if it's positive.Convenience functionsHaving to build aprocess_funcforask()would be cumbersome.
I created various convenience functions which ask users common questions.
All these convenience functions take the same arguments asask(),
but some may require additional arguments (see below).
All the convenience functions are available at the top level of the package
(as in, can be called withpyask.decimalrather thanpyask.numeric.decimal.Number convenience functions inpyask.numericdecimal()decimal(question, aid='enter a decimal', **kwargs)Asks user for a decimal (aka float). Example would be calling:>>> pyask.decimal("How tall are you", aid="in cm", default=5)
How tall are you? (in cm) [5]
5.0

>>> pyask.decimal("How tall are you", aid="in cm", default=5)
How tall are you? (in cm) [5] 7.8
7.8integer()integer(question, aid='enter an integer', **kwargs)Asks user for an integer (a float/decimal response is illegal and not accepted).number()number(question, aid='enter a number', **kwargs)Asks user for any number, and returns an integer if an integer was given
or a float if a float was given.number_between()number_between(l, r, question, aid='', only_int=False, **kwargs)Asks user for a number between l and r, inclusive.only_intallows you to accept only an integer response
ifTrueor accept decimals/floats ifFalse(default).number_eq_to()number_eq_to(n, question, aid='enter the correct number', **kwargs)Asks user for number equal to n.number_greater_than()number_greater_than(l, question, aid='', only_int=False, **kwargs)Asks user for number greater than l.only_intallows you to accept only an integer response
ifTrueor accept decimals/floats ifFalse(default).number_greater_than_or_eq()number_greater_than_or_eq(l, question, aid='', only_int=False, **kwargs)Asks user for number greater than or equal to l.only_intallows you to accept only an integer response
ifTrueor accept decimals/floats ifFalse(default).number_less_than()number_less_than(r, question, aid='', only_int=False, **kwargs)Asks user for number less than r.only_intallows you to accept only an integer response
ifTrueor accept decimals/floats ifFalse(default).number_less_than_or_eq()number_less_than_or_eq(r, question, aid='', only_int=False, **kwargs)Asks user for number less than or equal to r.seconds()seconds(question, aid='enter time in HH:MM:SS, MM:SS, or SS format', **kwargs)Asks user for time. Time can be in HH:MM:SS, MM:SS, or SS format.
SS format takes any number of positive seconds.
MM:SS and HH:MM:SS must have MM and SS between 0 and 59 (inclusive).
HH can be any positive number or zero.
It returns the time (seconds).Filepath convenience functions inpyask.pathdirname()dirname(question, aid='directory name', absolute=False, must_exist=False, **kwargs)Asks user for directory name, returns path.absolutewhenTruewill return an absolute path rather than a relative one.must_existwhenTruewill only let the user enter in the path of an existing directory.filename()filename(question, aid='filename', absolute=False, must_exist=False, **kwargs)Asks user for filename, returns path.absolutewhenTruewill return an absolute path rather than a relative one.must_existwhenTruewill only let the user enter in the path of an existing file.pathname()pathname(question, aid='pathname', absolute=False, must_exist=False, **kwargs)Asks user for pathname (either file or directory), returns path.absolutewhenTruewill return an absolute path rather than a relative one.must_existwhenTruewill only let the user enter in the path of an existing file or directory.Selection convenience functions inpyask.selectionContains functions that prompt a user to select from a list of questions.which()which(xs, question, aid='pick a number', **kwargs)Ask user to select an item from the sequencexsand returns that item.Example, with a default answer of 2 and the user hittingEnterto use the default answer:>>> pyask.which(['a', 'b', 'c'], "Which letter?", default="2")
Available choices are:
(0) a
(1) b
(2) c
Which letter? (pick a number) [2]
'c'which_items()which_items(xs, question, aid='space/comma separated number(s)', allow_repeats=True, **kwargs)Ask user to select multiple items from the sequencexs,
by specifying a comma or space delimited response.
Ifallow_repeatsisTrue, they can specify the same item multiple times.Example:>>> pyask.which_items(os.listdir(), "What videos, in order, should be stitched together?")
Available choices are:
(0) video1.mp4
(1) video3.mp4
(2) usb.txt
(3) video2.mp4
(4) video_4.mp4
What videos, in order, should be stitched together? (space/comma separated number(s), repeats ok) [] 0, 3, 1, 4
['video1.mp4', 'video2.mp4', 'video3.mp4', 'video_4.mp4']String convenience functions inpyask.stringContains functions that prompt a user to select from a list of questions.any_string()any_string(question, aid='any string', **kwargs)Asks user for a string response.char()char(question, aid='single character', **kwargs)Asks user for a single character response.string_regex()string_regex(regex, question, case_insensitive=False, **kwargs)Asks user for a string response that must completely match the regex.
The regex must be in standardPython regex.
This allows you to easily ask any possible variant of a question
and validate it with any regex you can think of.
The example below checks for a string that ends in "mp4", "avi", or "mov" and is case-insensitive:>>> pyask.string_regex(r"(?i).+(mp4|avi|mov)$", "What is a valid video filename")
What is a valid video filename? [] myvideo.docx
Invalid respose.
What is a valid video filename? [] myvideo.MP4
'myvideo.MP4'
>>> pyask.string_regex(r"(?i).+(mp4|avi|mov)$", "What is a valid video filename")
What is a valid video filename? [] myvideo.mp4
'myvideo.mp4'yes_no()yes_no(question, aid='yes or no', **kwargs)Asks user a yes or no question.
Accepts "yes", "y", "no", or "n" in any case (so "YES" or "YeS" works).
Returns a boolean. Nice to use in control-flow logic.Example:>>> if pyask.yes_no("Are you hungry"):
...     print("Here's a snack.")
... else:
...     print("You don't need a snack.")
...
Are you hungry? (yes or no) [] yes
Here's a snack.
>>>Contributions/suggestionsContributions and suggestions are welcome.
Feel free to raise anissuefor a feature request or
to submit apull requestif you have implemented features you would like to see inpyask.