Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python -msphinx
SPHINXBUILD = python3 -msphinx
SPHINXPROJ = prompt
SOURCEDIR = .
BUILDDIR = _build
Expand All @@ -17,4 +17,4 @@ help:
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
221 changes: 182 additions & 39 deletions prompt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"""Regular expression for email addresses."""


def character(prompt=None, empty=False):
def character(prompt=None, empty=False, default=None):
"""Prompt a single character.

Parameters
Expand All @@ -52,6 +52,8 @@ def character(prompt=None, empty=False):
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
default : str, optional
Value to return if response is empty.

Returns
-------
Expand All @@ -60,16 +62,19 @@ def character(prompt=None, empty=False):
None if the user pressed only Enter and ``empty`` was True.

"""
s = _prompt_input(prompt)
if empty and not s:
return None
s = input(_make_prompt(prompt, default))
if not s:
if default is not None:
return default
elif empty:
return None
elif len(s) == 1:
return s
else:
return character(prompt=prompt, empty=empty)


def email(prompt=None, empty=False, mode="simple"):
def email(prompt=None, empty=False, mode="simple", default=None):
"""Prompt an email address.

This check is based on a simple regular expression and does not verify
Expand All @@ -84,6 +89,8 @@ def email(prompt=None, empty=False, mode="simple"):
mode : {'simple'}, optional
'simple' will use a simple regular expression.
No other mode is implemented yet.
default : str, optional
Value to return if response is empty.

Returns
-------
Expand All @@ -93,9 +100,12 @@ def email(prompt=None, empty=False, mode="simple"):

"""
if mode == "simple":
s = _prompt_input(prompt)
if empty and not s:
return None
s = input(_make_prompt(prompt, default))
if not s:
if default is not None:
return default
elif empty:
return None
else:
if RE_EMAIL_SIMPLE.match(s):
return s
Expand All @@ -105,7 +115,7 @@ def email(prompt=None, empty=False, mode="simple"):
raise ValueError


def integer(prompt=None, empty=False):
def integer(prompt=None, empty=False, default=None):
"""Prompt an integer.

Parameters
Expand All @@ -114,6 +124,8 @@ def integer(prompt=None, empty=False):
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
default : int, optional
Value to return if response is empty.

Returns
-------
Expand All @@ -122,17 +134,20 @@ def integer(prompt=None, empty=False):
None if the user pressed only Enter and ``empty`` was True.

"""
s = _prompt_input(prompt)
if empty and not s:
return None
s = input(_make_prompt(prompt, default))
if not s:
if default is not None:
return default
elif empty:
return None
else:
try:
return int(s)
except ValueError:
return integer(prompt=prompt, empty=empty)


def real(prompt=None, empty=False):
def real(prompt=None, empty=False, default=None):
"""Prompt a real number.

Parameters
Expand All @@ -141,6 +156,8 @@ def real(prompt=None, empty=False):
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
default : float, optional
Value to return if response is empty.

Returns
-------
Expand All @@ -149,17 +166,20 @@ def real(prompt=None, empty=False):
None if the user pressed only Enter and ``empty`` was True.

"""
s = _prompt_input(prompt)
if empty and not s:
return None
s = input(_make_prompt(prompt, default))
if not s:
if default is not None:
return default
elif empty:
return None
else:
try:
return float(s)
except ValueError:
return real(prompt=prompt, empty=empty)


def regex(pattern, prompt=None, empty=False, flags=0):
def regex(pattern, prompt=None, empty=False, flags=0, default=None):
"""Prompt a string that matches a regular expression.

Parameters
Expand All @@ -172,6 +192,8 @@ def regex(pattern, prompt=None, empty=False, flags=0):
Allow an empty response.
flags : int, optional
Flags that will be passed to ``re.match``.
default : str, optional
Value to substitute as input if response is empty.

Returns
-------
Expand All @@ -184,18 +206,21 @@ def regex(pattern, prompt=None, empty=False, flags=0):
re.match

"""
s = _prompt_input(prompt)
if empty and not s:
return None
s = input(_make_prompt(prompt, default))
if not s:
if default is not None:
s = default
elif empty:
return None

m = re.match(pattern, s, flags=flags)
if m:
return m
else:
m = re.match(pattern, s, flags=flags)
if m:
return m
else:
return regex(pattern, prompt=prompt, empty=empty, flags=flags)
return regex(pattern, prompt=prompt, empty=empty, flags=flags)


def secret(prompt=None, empty=False):
def secret(prompt=None, empty=False, default=None):
"""Prompt a string without echoing.

Parameters
Expand All @@ -204,6 +229,8 @@ def secret(prompt=None, empty=False):
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
default : float, optional
Value to return if response is empty.

Returns
-------
Expand All @@ -221,19 +248,20 @@ def secret(prompt=None, empty=False):
getpass.getpass

"""
if prompt is None:
prompt = PROMPT
s = getpass.getpass(prompt=prompt)
if empty and not s:
return None
s = getpass.getpass(prompt=_make_prompt(prompt, default))
if not s:
if default is not None:
return default
elif empty:
return None
else:
if s:
return s
else:
return secret(prompt=prompt, empty=empty)


def string(prompt=None, empty=False):
def string(prompt=None, empty=False, default=None):
"""Prompt a string.

Parameters
Expand All @@ -242,6 +270,8 @@ def string(prompt=None, empty=False):
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
default : float, optional
Value to return if response is empty.

Returns
-------
Expand All @@ -250,18 +280,131 @@ def string(prompt=None, empty=False):
None if the user pressed only Enter and ``empty`` was True.

"""
s = _prompt_input(prompt)
if empty and not s:
return None
s = input(_make_prompt(prompt, default))
if not s:
if default is not None:
return default
elif empty:
return None
else:
if s:
return s
else:
return string(prompt=prompt, empty=empty)


def _prompt_input(prompt):
def choice(choices, instruction='Select one of the following: ',
prompt=None, empty=False, default=None):
"""Prompt for a selection from constrained set of choices.

Parameters
----------
choices : iterable of str
Ordered choices for user to select
instruction : str, optional
Text to appear above available choices
prompt : str, optional
Use an alternative prompt.
empty : bool, optional
Allow an empty response
default : float, optional
Value (found in ``choices``) to return if response is empty.

Raises
------
ValueError
If choices is not a sequence of one or more values

Returns
-------
An item from ``choices`` if the user selected a choice.
None if the user pressed only Enter and ``empty`` was True.

"""
choices = tuple(choices)
if len(choices) < 1:
raise ValueError('Need minimum of one choice!')

print(instruction)
for num, cho in enumerate(choices):
print(' {n}: {c}'.format(n=(num + 1), c=cho))

s = input(_make_prompt(prompt, default))

try:
num = int(s) - 1
if num < 0:
raise ValueError
return choices[num]
except (ValueError, IndexError):
if not s:
if default is not None:
return default
elif empty:
return None
else:
return choice(choices, instruction, prompt, empty)


def boolean(prompt=None, yes='y', no='n', default=None, sensitive=False,
partial=True):
"""Prompt for a yes/no response.

Parameters
----------
prompt : str, optional
Use an alternative prompt.
yes : str, optional
Response corresponding to 'yes'.
no : str, optional
Response correspnding to 'no'.
default : bool, optional
The return value if user inputs empty response.
sensitive : bool, optional
If True, input is case sensitive.
partial : bool, optional
Can user type 'y' or 'ye' for 'yes' and 'n' for 'no'?

Returns
-------
bool
Either True (if user selects 'yes') or False (if user selects 'no')

"""
def norm(x):
return x if sensitive else str(x).lower()

def to_bool(c):
"""Business logic for converting input to boolean."""
if partial and len(c):
if norm(yes).startswith(norm(c)):
return True
elif norm(no).startswith(norm(c)):
return False
else:
if norm(yes) == norm(c):
return True
elif norm(no) == norm(c):
return False
raise ValueError

if prompt is None:
return input(PROMPT)
else:
return input(prompt)
y = '[{}]'.format(yes) if default is True else yes
n = '[{}]'.format(no) if default is False else no
prompt = '{y}/{n}? '.format(y=y, n=n)

s = input(prompt)
if (default is not None) and not s:
return default

try:
return to_bool(s)
except ValueError:
return boolean(prompt=prompt, yes=yes, no=no, default=default,
sensitive=sensitive, partial=partial)


def _make_prompt(prompt, default):
if not prompt:
prompt = '[{}]? '.format(default) if default else PROMPT
return prompt
Loading