How to set a default editable string for raw_input? - python

I'm using Python 2.7's raw_input to read from stdin.
I want to let the user change a given default string.
Code:
i = raw_input("Please enter name:")
Console:
Please enter name: Jack
The user should be presented with Jack but can change (backspace) it to something else.
The Please enter name: argument would be the prompt for raw_input and that part shouldn't be changeable by the user.

You could do:
i = raw_input("Please enter name[Jack]:") or "Jack"
This way, if user just presses return without entering anything, "i" will be assigned "Jack".

Python2.7 get raw_input and set a default value:
Put this in a file called a.py:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return raw_input(prompt)
finally:
readline.set_startup_hook()
default_value = "an insecticide"
stuff = rlinput("Caffeine is: ", default_value)
print("final answer: " + stuff)
Run the program, it stops and presents the user with this:
el#defiant ~ $ python2.7 a.py
Caffeine is: an insecticide
The cursor is at the end, user presses backspace until 'an insecticide' is gone, types something else, then presses enter:
el#defiant ~ $ python2.7 a.py
Caffeine is: water soluable
Program finishes like this, final answer gets what the user typed:
el#defiant ~ $ python2.7 a.py
Caffeine is: water soluable
final answer: water soluable
Equivalent to above, but works in Python3:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
default_value = "an insecticide"
stuff = rlinput("Caffeine is: ", default_value)
print("final answer: " + stuff)
More info on what's going on here:
https://stackoverflow.com/a/2533142/445131

In dheerosaur's answer If user press Enter to select default value in reality it wont be saved as python considers it as '' string so Extending a bit on what dheerosaur.
default = "Jack"
user_input = raw_input("Please enter name: %s"%default + chr(8)*4)
if not user_input:
user_input = default
Fyi .. The ASCII value of backspace is 08

I only add this because you should write a simple function for reuse. Here is the one I wrote:
def default_input( message, defaultVal ):
if defaultVal:
return raw_input( "%s [%s]:" % (message,defaultVal) ) or defaultVal
else:
return raw_input( "%s " % (message) )

On platforms with readline, you can use the method described here: https://stackoverflow.com/a/2533142/1090657
On Windows, you can use the msvcrt module:
from msvcrt import getch, putch
def putstr(str):
for c in str:
putch(c)
def input(prompt, default=None):
putstr(prompt)
if default is None:
data = []
else:
data = list(default)
putstr(data)
while True:
c = getch()
if c in '\r\n':
break
elif c == '\003': # Ctrl-C
putstr('\r\n')
raise KeyboardInterrupt
elif c == '\b': # Backspace
if data:
putstr('\b \b') # Backspace and wipe the character cell
data.pop()
elif c in '\0\xe0': # Special keys
getch()
else:
putch(c)
data.append(c)
putstr('\r\n')
return ''.join(data)
Note that arrows keys don't work for the windows version, when it's used, nothing will happen.

For windows users with gitbash/msys2 or cygwin you can use it's built in readline through python subprocess. It is a sort of hack but works quite well and doesn't require any third party code. For personal tools this works really well.
Msys2 specific: If you want ctrl+c to immediately exit, you will need to run your program with
winpty python program.py
import subprocess
import shlex
def inputMsysOrCygwin(prompt = "", prefilled = ""):
"""Run your program with winpty python program.py if you want ctrl+c to behave properly while in subprocess"""
try:
bashCmd = "read -e -p {} -i {} bash_input; printf '%s' \"$bash_input\"".format(shlex.quote(prompt), shlex.quote(prefilled))
userInput = subprocess.check_output(["sh", "-c", bashCmd], encoding='utf-8')
return userInput
except FileNotFoundError:
raise FileNotFoundError("Invalid environment: inputMsysOrCygwin can only be run from bash where 'read' is available.")
userInput = ""
try:
#cygwin or msys2 shell
userInput = inputMsysOrCygwin("Prompt: ", "This is default text")
except FileNotFoundError:
#cmd or powershell context where bash and read are not available
userInput = input("Prompt [This is default text]: ") or "This is default text"
print("userInput={}".format(userInput))

Try this: raw_input("Please enter name: Jack" + chr(8)*4)
The ASCII value of backspace is 08.

Related

Redirecting stdin and stdout

When I run the command
python3 ./db.py 'blah blah blah' > output.html
The text "Enter your name: Enter your password:" appears in output.html. I do not want this to be there. It's accepting username and password but it isn't prompting the command line with "Enter your name". Any idea how I fix this?
This is the code I'm running:
import psycopg2
import sys
name = input("Enter your name: ")
passwd = input("Enter your password: ")
You could try redirecting your input calls to stderr. I recommend using contextlib so that all calls are redirected without having to specify file= every time. Here's a minimal example:
import contextlib
import sys
name, passwd = None, None
with contextlib.redirect_stdout(sys.stderr):
print("This does not appear in stdout.")
name = input("Please enter your name: ")
passwd = input("Please enter your password: ")
print("This still appears in stdout.")
print(f"name = {name}")
print(f"pass = {passwd}")
And when running:
$ python ./temp.py > temp-out.txt
This does not appear in stdout.
Please enter your name: Matt
Please enter your password: abc
$ cat ./temp-out.txt
This still appears in stdout.
name = Matt
pass = abc
Per my comment, however, I would suggest doing the writing in your actual Python. Try passing the desired output filename as a parameter/argument to the script.
When you use the input(prompt) function, the contents of prompt will be sent to standard output. That's in the documentation for input():
input?
Signature: input(prompt=None, /)
Docstring:
Read a string from standard input. The trailing newline is stripped.
The prompt string, if given, is printed to standard output without a
trailing newline before reading input.
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, readline is used if available.
Type: builtin_function_or_method
If you wish the result to be written to a file, you should do that in the code itself, rather than redirecting stdout to a file.
with open(filename, 'w') as file:
file.write(name+'\n')
file.write(passwd+'\n')
Just use stderr instead of stdout:
print("Enter your password: ", file=sys.stderr, flush=True)
password = input()
This way you'll have both prompt and clean output redirected to a file.

How to edit a string in terminal, Python [duplicate]

I'm using Python 2.7's raw_input to read from stdin.
I want to let the user change a given default string.
Code:
i = raw_input("Please enter name:")
Console:
Please enter name: Jack
The user should be presented with Jack but can change (backspace) it to something else.
The Please enter name: argument would be the prompt for raw_input and that part shouldn't be changeable by the user.
You could do:
i = raw_input("Please enter name[Jack]:") or "Jack"
This way, if user just presses return without entering anything, "i" will be assigned "Jack".
Python2.7 get raw_input and set a default value:
Put this in a file called a.py:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return raw_input(prompt)
finally:
readline.set_startup_hook()
default_value = "an insecticide"
stuff = rlinput("Caffeine is: ", default_value)
print("final answer: " + stuff)
Run the program, it stops and presents the user with this:
el#defiant ~ $ python2.7 a.py
Caffeine is: an insecticide
The cursor is at the end, user presses backspace until 'an insecticide' is gone, types something else, then presses enter:
el#defiant ~ $ python2.7 a.py
Caffeine is: water soluable
Program finishes like this, final answer gets what the user typed:
el#defiant ~ $ python2.7 a.py
Caffeine is: water soluable
final answer: water soluable
Equivalent to above, but works in Python3:
import readline
def rlinput(prompt, prefill=''):
readline.set_startup_hook(lambda: readline.insert_text(prefill))
try:
return input(prompt)
finally:
readline.set_startup_hook()
default_value = "an insecticide"
stuff = rlinput("Caffeine is: ", default_value)
print("final answer: " + stuff)
More info on what's going on here:
https://stackoverflow.com/a/2533142/445131
In dheerosaur's answer If user press Enter to select default value in reality it wont be saved as python considers it as '' string so Extending a bit on what dheerosaur.
default = "Jack"
user_input = raw_input("Please enter name: %s"%default + chr(8)*4)
if not user_input:
user_input = default
Fyi .. The ASCII value of backspace is 08
I only add this because you should write a simple function for reuse. Here is the one I wrote:
def default_input( message, defaultVal ):
if defaultVal:
return raw_input( "%s [%s]:" % (message,defaultVal) ) or defaultVal
else:
return raw_input( "%s " % (message) )
On platforms with readline, you can use the method described here: https://stackoverflow.com/a/2533142/1090657
On Windows, you can use the msvcrt module:
from msvcrt import getch, putch
def putstr(str):
for c in str:
putch(c)
def input(prompt, default=None):
putstr(prompt)
if default is None:
data = []
else:
data = list(default)
putstr(data)
while True:
c = getch()
if c in '\r\n':
break
elif c == '\003': # Ctrl-C
putstr('\r\n')
raise KeyboardInterrupt
elif c == '\b': # Backspace
if data:
putstr('\b \b') # Backspace and wipe the character cell
data.pop()
elif c in '\0\xe0': # Special keys
getch()
else:
putch(c)
data.append(c)
putstr('\r\n')
return ''.join(data)
Note that arrows keys don't work for the windows version, when it's used, nothing will happen.
For windows users with gitbash/msys2 or cygwin you can use it's built in readline through python subprocess. It is a sort of hack but works quite well and doesn't require any third party code. For personal tools this works really well.
Msys2 specific: If you want ctrl+c to immediately exit, you will need to run your program with
winpty python program.py
import subprocess
import shlex
def inputMsysOrCygwin(prompt = "", prefilled = ""):
"""Run your program with winpty python program.py if you want ctrl+c to behave properly while in subprocess"""
try:
bashCmd = "read -e -p {} -i {} bash_input; printf '%s' \"$bash_input\"".format(shlex.quote(prompt), shlex.quote(prefilled))
userInput = subprocess.check_output(["sh", "-c", bashCmd], encoding='utf-8')
return userInput
except FileNotFoundError:
raise FileNotFoundError("Invalid environment: inputMsysOrCygwin can only be run from bash where 'read' is available.")
userInput = ""
try:
#cygwin or msys2 shell
userInput = inputMsysOrCygwin("Prompt: ", "This is default text")
except FileNotFoundError:
#cmd or powershell context where bash and read are not available
userInput = input("Prompt [This is default text]: ") or "This is default text"
print("userInput={}".format(userInput))
Try this: raw_input("Please enter name: Jack" + chr(8)*4)
The ASCII value of backspace is 08.

Issues with input() and raw_input() for Python 2.7. User entered data is not read properly. What is going on?

So at my company they are making me use python 2.7 because of the product for a compatibility reason that I won't get into here.
So I am writing a program that connects to a device using SSH (a switch specifically) and I am able to actually access the device using SSH and this device is ping-able on my machine. The problem ? raw_input seems to not be taking it as a string. When I try input(), it gives me an invalid syntax error.
For the scripts I write, I usually use arparse and the user enters the IP address, username, and password through the terminal, but I want this script to not use argparse and to use input() or raw_input. All my SSH scripts work good except for this one, the only one using raw_input and input() instead of argparse
def runMain():
scriptName = os.path.basename(__file__)
print("The name of this script is: " + scriptName)
print("**************************************\n")
print("This script allows you to enable and disable ports on the SNET or SOOBM switches, have fun ! \n")
print("**************************************\n")
optionPrinter_switches_top()
user_input = raw_input("Make your selection") # This does not work if I change to input(), it exits out of the program
if user_input == 1:
print("You selected the SNET switch, lets proceed !")
deviceIP = input("Enter the IP address for this device") # I tried changing these to raw_input, I get a syntax issue
deviceUsername = input("Enter the username for this device")
devicePassword = input("Enter the password for this device")
confirm_ping = canPing(deviceIP) # This is a boolean function that works correctly in every script but this one.
if confirm_ping:
ssh_SNET = connectToSSH_SNET(deviceIP, deviceUsername, devicePassword)
else:
print("Sorry, that device is not even ping-able. Figure that issue out and retry the program...")
sys.exit(-1)
while True:
SNET_options()
user_choice_SNET = input("Please select an option")
switch_result = SNET_switch_func(user_choice_SNET)
if switch_result == "displayInterfaceBrief":
time.sleep(5)
displayInterfaceBrief_SNET(ssh_SNET)
elif switch_result == "enablePort":
time.sleep(5)
enablePort_SNET(ssh_SNET)
elif switch_result == "disablePort":
disablePort_SNET(ssh_SNET)
elif switch_result == "switchReboot":
reboot_SNET_switch(ssh_SNET)
else:
print("Exiting program now....")
sys.exit(-1)
Here are relevant issues:
user_input = raw_input("Make your selection") # This does not work if I change to input(), it exits out of the program
deviceIP = input("Enter the IP address for this device") # I tried changing these to raw_input, I get a syntax issue
deviceUsername = input("Enter the username for this device")
devicePassword = input("Enter the password for this device")
confirm_ping = canPing(deviceIP) # This is a boolean function that works correctly in every script but this one.
Conclusion ? There is an issue with input()/raw_input() . What is going on here and how can I fix this ? I can't use python 3.7 and it really is frustrating. Thanks for the help
Try changing if user_input == 1: to if int(user_input) == 1: as the input function takes an input in string format, by default.
And, if you want to use input() instead of raw_input() to take input from users in python 2.x, then you can try below code:
if hasattr(__builtins__, 'raw_input'):
input=raw_input
user_input = input("Enter a number: ")

Read a data from terminal within a script

I wanted to read a variable from terminal by running a script.
This is my script.py:
while True:
value = input('enter text: ')
if value == 'stop':
print('bye-bye')
break
else:
print('continue!')
However, when I ran python script.py, something weird happened.
If i entered int-data (for instance 1,2,3), there was no problem.
If i entered 'stop', I just got an error:
SyntaxError: invalid syntax
def console(input_):
input_ = input(r'Enter text: ')
if input_ == str(r'stop'):
return ("BYE BYE")
else:
input_ = input('continue')
console(input_)
console(input)
Works fine on python3.
You are probably using Python2. In Python2 input() returns an int, for having a string you should use raw_input():
while True:
value = raw_input('enter text: ')
if value == 'stop':
print('bye-bye')
break
else:
print('continue!')
Do not use input() in Python2 since it actually evaluates the input, and it can be dangerous. See this other question for the difference from Python3 and Python2 input().
When you change to raw_input(), be sure to save before calling the script

Python prompt for user with echo and password without echo

Following code prompts for user and password, when run in console:
import getpass
user = getpass.getpass("Username:")
passwd = getpass.getpass("Password for " + user + ":")
print "Got", user, passwd
The obvious problem with above is, user name is not echoed as it is typed.
Now getpass documentation says "On Unix it defaults to using /dev/tty before falling back to sys.stdin and sys.stderr."
Question: How to ask for both username and password, so that they are read from same source, and username is echoed normally, while password is not?
Why not just use raw_input for the username:
import getpass
user = raw_input("Username:")
passwd = getpass.getpass("Password for " + user + ":")
print("Got", user, passwd)
Demo:
Username:iCodez
Password for iCodez:
('Got', 'iCodez', 'secret')
There is another alternative, which I found documented here. Detect whether the input stream is a TTY, and change your input method based on that information.
I used something like this:
#!/usr/bin/python
import sys
import getpass
if sys.stdin.isatty():
print "Enter credentials"
username = raw_input("Username: ")
password = getpass.getpass("Password: ")
else:
username = sys.stdin.readline().rstrip()
password = sys.stdin.readline().rstrip()
print "Username: [%s], password [%s]" % (username, password)
This works fine from a terminal:
bash> ./mytest.py
Enter credentials
Username: one
Password:
Username: [one], password [two]
for piped input:
bash> echo "one
> two" | ./mytest.py
Username: [one], password [two]
for input from a file:
bash> echo "one" > input
bash> echo "two" >> input
bash> ./mytest.py < input
Username: [one], password [two]
and also for a heredoc:
bash> ./mytest.py << EOF
> one
> two
> EOF
Username: [one], password [two]
Personally, that covers all of my needs.
In Python 2.x, use raw_input (input in Python 3.x) instead of getpass.getpass for username.
user = raw_input("Username:")
It would be possible to use raw_input (input in Python 3) but as already mentioned in the question, getpass is using /dev/tty before falling back to sys.stdin and sys.stderr. This means that in some situations getpass and raw_input is using different sources.
On linux, you might see the difference by executing the application with the following command:
my_app.py < /path/to/some/file
The function raw_input would read from the file while getpass would still use the terminal. Even if it is not documented explicitly, the same is happening on Windows.
I have not found a function that is doing something like getpass without hiding the input. I think you have to implement it yourself or search for a library which is doing it. You can look at the current implementation of getpass in Python 3 and in Python 2.7 to get some inspiration.
I wrote an exmaple below. Basically, I used the implementation of Python 3 and removed everything that is related to hiding the input. Then, I made some changes to support Python 2.
def _raw_input(prompt, fin, fout):
if prompt:
try:
fout.write(prompt)
except UnicodeEncodeError:
# Replace characters that are not supported by the terminal
prompt = prompt.encode(fout.encoding, 'replace')
prompt = prompt.decode(fout.encoding)
fout.write(prompt)
fout.flush()
line = fin.readline()
return line[:-1] if line[-1] == '\n' else line
def _ttyinput_unix(prompt):
try:
fd = os.open("/dev/tty", os.O_RDWR, os.O_NOCTTY)
if sys.version_info < (3, 0):
with os.fdopen(fd, 'w+', 1) as tty:
return _raw_input(prompt, tty, tty)
with io.FileIO(fd, 'w+') as tty:
with io.TextIOWrapper(tty) as wrapper:
return _raw_input(prompt, wrapper, wrapper)
except (OSError, AttributeError) as e:
return _raw_input(prompt, sys.stdin, sys.stderr)
def _ttyinput_win(prompt):
if sys.stdin is not sys.__stdin__:
# I don't know why getpass is doing this.
return _raw_input(prompt, sys.stdin, sys.stderr)
if sys.version_info >= (3, 0):
getch = msvcrt.getwch
putch = msvcrt.putwch
else:
getch = msvcrt.getch
putch = msvcrt.putch
for c in prompt:
putch(c)
password = ""
while True:
c = getch()
if c == '\r' or c == '\n':
break
if c == '\003':
raise KeyboardInterrupt
if c == '\b':
if len(password) > 0:
password = password[:-1]
for x in "\b \b":
putch(x)
else:
password += c
putch(c)
putch('\r')
putch('\n')
return password
try:
import msvcrt
ttyinput = _ttyinput_win
except ImportError:
ttyinput = _ttyinput_unix
I tested my implementation with Python 2.7 on Windows and with Python 2.7 and 3.5 on Arch Linux.
try this:
user = raw_input("Username:")

Categories