# The MIT License (MIT)
#
# Copyright (c) 2015-2017 Stefan Fischer
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
"""Prompt and verify user input on the command line.
The project was initiated by Stefan Fischer.
Note
----
This project is still a work in progress.
"""
import getpass
import re
__author__ = "Stefan Fischer"
__contact__ = "Stefan Fischer <sfischer13@ymail.com>"
__copyright__ = "Copyright (c) 2015-2017 Stefan Fischer"
__credits__ = []
__date__ = "2017-06-05"
__license__ = "MIT"
__status__ = "development"
__version__ = "0.4.1"
PROMPT = "? "
"""Prompt that will be shown by default."""
RE_EMAIL_SIMPLE = re.compile(r"^[^@]+@[^@]+\.[^@]+$")
"""Regular expression for email addresses."""
[docs]def character(prompt=None, empty=False):
"""Prompt a single character.
Parameters
----------
prompt : str, optional
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
Returns
-------
str or None
A str if the user entered a single-character, non-empty string.
None if the user pressed only Enter and ``empty`` was True.
"""
s = _prompt_input(prompt)
if empty and not s:
return None
elif len(s) == 1:
return s
else:
return character(prompt=prompt, empty=empty)
[docs]def email(prompt=None, empty=False, mode="simple"):
"""Prompt an email address.
This check is based on a simple regular expression and does not verify
whether an email actually exists.
Parameters
----------
prompt : str, optional
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
mode : {'simple'}, optional
'simple' will use a simple regular expression.
No other mode is implemented yet.
Returns
-------
str or None
A str if the user entered a likely email address.
None if the user pressed only Enter and ``empty`` was True.
"""
if mode == "simple":
s = _prompt_input(prompt)
if empty and not s:
return None
else:
if RE_EMAIL_SIMPLE.match(s):
return s
else:
return email(prompt=prompt, empty=empty, mode=mode)
else:
raise ValueError
[docs]def integer(prompt=None, empty=False):
"""Prompt an integer.
Parameters
----------
prompt : str, optional
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
Returns
-------
int or None
An int if the user entered a valid integer.
None if the user pressed only Enter and ``empty`` was True.
"""
s = _prompt_input(prompt)
if empty and not s:
return None
else:
try:
return int(s)
except ValueError:
return integer(prompt=prompt, empty=empty)
[docs]def real(prompt=None, empty=False):
"""Prompt a real number.
Parameters
----------
prompt : str, optional
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
Returns
-------
float or None
A float if the user entered a valid real number.
None if the user pressed only Enter and ``empty`` was True.
"""
s = _prompt_input(prompt)
if empty and not s:
return None
else:
try:
return float(s)
except ValueError:
return real(prompt=prompt, empty=empty)
[docs]def regex(pattern, prompt=None, empty=False, flags=0):
"""Prompt a string that matches a regular expression.
Parameters
----------
pattern : str
A regular expression that must be matched.
prompt : str, optional
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
flags : int, optional
Flags that will be passed to ``re.match``.
Returns
-------
Match or None
A match object if the user entered a matching string.
None if the user pressed only Enter and ``empty`` was True.
See Also
--------
re.match
"""
s = _prompt_input(prompt)
if empty and not s:
return None
else:
m = re.match(pattern, s, flags=flags)
if m:
return m
else:
return regex(pattern, prompt=prompt, empty=empty, flags=flags)
[docs]def secret(prompt=None, empty=False):
"""Prompt a string without echoing.
Parameters
----------
prompt : str, optional
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
Returns
-------
str or None
A str if the user entered a non-empty string.
None if the user pressed only Enter and ``empty`` was True.
Raises
------
getpass.GetPassWarning
If echo free input is unavailable.
See Also
--------
getpass.getpass
"""
if prompt is None:
prompt = PROMPT
s = getpass.getpass(prompt=prompt)
if empty and not s:
return None
else:
if s:
return s
else:
return secret(prompt=prompt, empty=empty)
[docs]def string(prompt=None, empty=False):
"""Prompt a string.
Parameters
----------
prompt : str, optional
Use an alternative prompt.
empty : bool, optional
Allow an empty response.
Returns
-------
str or None
A str if the user entered a non-empty string.
None if the user pressed only Enter and ``empty`` was True.
"""
s = _prompt_input(prompt)
if empty and not s:
return None
else:
if s:
return s
else:
return string(prompt=prompt, empty=empty)
def _prompt_input(prompt):
if prompt is None:
return input(PROMPT)
else:
return input(prompt)