I'm relatively new to socket programming and stdin / stdout and I need to know if there is a way to take input from STDIN before the user hits enter.
For example, if they type 'F' but don't hit enter, I need to see that 'F'.
I need this for a basic chatting app, and I need the user's typing to go to the next line so I can print incoming messages without breaking their text.
I've tried freezing STDOUT until the user is done typing, but this just freezes the minute the code reaches the input code block.
Current code:
### Main Handle ###
def Handler():
print("\nConnected.\n")
print(f"{server.recv(1028).decode()}\n")
def _send():
while True:
sys.stdout = buffer = io.StringIO()
text = input()
sys.stdout = old_stdout
print(buffer.getvalue())
try:
server.sendall(text.encode())
except:
pass
def _get():
while True:
print(stdin)
try:
message = server.recv(1028).decode()
if message == "":
print("\nYou got disconnected from the server, sorry bud. :C")
break
except ConnectionResetError:
print("\nYou got disconnected from the server, sorry bud. :C")
break
print(f"\n{message}")
_gthread = Thread(target = _get)
_gthread.start()
_sthread = Thread(target = _send)
_sthread.start()
while True:
pass
Any help at all is greatly appreciated. :)
The shell or the terminal emulator, or both, would ask, as part of their startup sequence, to put the terminal into "cooked" mode, which buffers input until it sees a new line. This buffering happens in the terminal driver, before it even reaches your program, which is why your program won't see individual characters in stdin.
You need to have your program put the terminal into it's raw (uncooked) mode, or a variation of raw mode that doesn't do buffering.
Python example.
Note that in the "real" raw mode, CTRL+C and friends don't work, so you probably don't want tty.setraw() but rather tty.setcbreak().
Related
There's a console program I want to run from a python script. Let me call it the child.
Once in a while, to continue processing, the child expects to read 0 bytes of data from stdin.
For simplicity, let's assume the child is the following python script:
child.py
import os
import sys
import time
stdin = sys.stdin.fileno()
def speak(message):
print(message, flush=True, end="")
while True:
speak("Please say nothing!")
data = os.read(stdin, 1024)
if data == b"":
speak("Thank you for nothing.")
time.sleep(5)
else:
speak("I won't continue unless you keep silent.")
To work successfully (i.e. seeing "Thank you for nothing." printed), you must typically hit Ctrl+D when running it in a UNIX terminal, while in cmd or PowerShell under Windows hitting Ctrl+Z followed by Enter will do the trick.
Here's an attempt to run the child from inside a python script, which I shall call the parent:
parent.py
import os
import subprocess
def speak_to_child(child_stdin_r, child_stdin_w, message):
child = subprocess.Popen(
["python", "child.py"],
stdin=child_stdin_r,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
child_stdout_r = child.stdout.fileno()
while True:
data = os.read(child_stdout_r, 1024)
print(f"child said: {data}")
if data == b"Please say nothing!":
os.write(child_stdin_w, message)
child_stdin_r, child_stdin_w = os.pipe()
speak_to_child(child_stdin_r, child_stdin_w, b"Not sure how to say nothing.")
This is of course an unsuccessful attempt as the child will clearly answer with "I won't continue unless you keep silent." after reading "Not sure how to say nothing." from its stdin.
Naively changing the message b"Not sure how to say nothing." in the parent to the empty message b"" doesn't get rid of the problem, since writing 0 bytes to a pipe won't cause a read of 0 bytes on the receiving end.
Now on UNIX we could easily solve the problem by replacing the pipe with a pseudoterminal and the empty message b"" with an EOT character b"\x04" like so:
import pty
child_stdin_w, child_stdin_r = pty.openpty()
speak_to_child(child_stdin_r, child_stdin_w, b"\x04")
This works ... but evidently not on Windows. So now my questions:
On UNIX, is using a pseudoterminal the best way to force the read of 0 bytes or is there a better way?
On Windows, given that pseudoterminals aren't available, how can I solve the problem?
A platform agnostic solution would of course be ideal.
This question already has answers here:
How to flush the input stream?
(4 answers)
Closed 1 year ago.
I'm making a game that runs on the console in python. When I exit the game, all the keys I pressed are automatically typed. How do I stop this from happening? Also, I still want to have user input when I use the input() function. This is on Windows by the way.
If you want the code, this has the same "problem":
for _ in range(100000):
print("Hello")
When the program finishes in the command prompt, this comes up:
C:\Users\User>awdsaawdsadwsaasdwaws
Basically, whatever keys were pressed while the code was running. This happens when other things run in the command prompt too, but I want to know how to disable it in python.
Edit: I kept digging and found that what I was looking for was flushing or clearing keyboard buffer. I marked my question as a duplicate of another which has a few answers, but this one worked best for me:
def flush_input():
try:
import msvcrt
while msvcrt.kbhit():
msvcrt.getch()
except ImportError:
import sys, termios #for linux/unix
termios.tcflush(sys.stdin, termios.TCIOFLUSH)
This happens because your computer registers the key strokes and on the console, those are made available on the stdin input stream.
If you save your script as test.py and run python test.py and start entering some keystrokes, like abc, those letters will be on standard input.
Your script doesn't read them, because it doesn't touch that stream, as you're not using input() or any other calls that would read that stream. So your script finishes, the characters are still on standard input, the prompt comes back and it reads those characters, with the given result:
Hello
Hello
Hello
PS C:\Users\username> abc
To avoid this, you can read / flush the input buffer at the end of your script. However, this is surprisingly hard if you need it to work across all operating systems and in different modes of running your script (directly from cmd, IDLE, in other IDEs, etc.)
The problem is there's no way to know if there's input on the standard input stream, until you try to read from it. But if you try to read from it, your script will pause until an 'end of line' or 'end of file' is received. And if the user is just hitting keys, that won't happen, so you'll end up reading until they hit something like Ctrl+Break or Ctrl+C.
Here's a way I think is relatively robust, but I recommend you test it in scenarios and environments you consider likely for use of your script:
import sys
import threading
import queue
import os
import signal
for _ in range(100000):
print("Hello")
timeout = 0.1 # sec
def no_input():
# stop main thread (which is probably blocked reading input) via an interrupt signal
# only available for windows in Python version 3.2 or higher
os.kill(os.getpid(), signal.SIGINT)
exit()
# if a sigint is received, exit the main thread (you could call another function to do work first and then exit()
signal.signal(signal.SIGINT, exit)
# input is stored here, until it's dealt with
input_queue = queue.Queue()
# read all available input until main thread exit
def get_input():
while True:
try:
# read input, not doing anything with it
_ = input_queue.get(timeout=timeout)
except queue.Empty:
no_input()
reading_thread = threading.Thread(target=get_input)
reading_thread.start()
# main loop: put any available input in the queue, will wait for input if there is none
for line in sys.stdin:
input_queue.put(line)
# wait for reading thread
reading_thread.join()
It basically reads the input from a second thread, allowing that the main thread to get the input and possibly do something with it until there's nothing left and then it just tells the main thread to exit. Note that this will result in your script exiting with an exit code of 2, which may not be what you want.
Also note that you'll still see the input on screen, but it will no longer be passed to the terminal:
Hello
Hello
Hello
abc
PS C:\Users\username>
I don't know if there's an easy way to avoid the echo, other than on Linux doing something like stty -echo. You could of course just call the system to clear the screen at the end of your script:
from subprocess import call
from os import name as os_name
call('clear' if os_name =='posix' else 'cls')
I am running jirashell in a python script using the subprocess library. I am currently having issues having the outputs print in real time. When I run jirashell it outputs information than prompts the user (y/n). The subprocess won't print out information prior to the prompt until I enter 'y' or 'n'.
The code I am using is
_consumer_key = "justin-git"
_cmd = "jirashell -s {0} -od -k {1} -ck {2} -pt".format(JIRA_SERVER,
_rsa_private_key_path, _consumer_key)
p = subprocess.Popen(_cmd.split(" "), stdout=subprocess.PIPE,
stderr=subprocess.PIPE, bufsize=0)
out, err = p.communicate() # Blocks here
print out
print err
The output is like so:
n # I enter a "n" before program will print output.
Output:
Request tokens received.
Request token: asqvavefafegagadggsgewgqqegqgqge
Request token secret: asdbresbdfbrebsaerbbsbdabweabfbb
Please visit this URL to authorize the OAuth request:
http://localhost:8000/plugins/servlet/oauth/authorize?oauth_token=zzzzzzzzzzzzzzzzzzzzzzzzzzzzz
Have you authorized this program to connect on your behalf to http://localhost:8000? (y/n)
Error:
Abandoning OAuth dance. Your partner faceplants. The audience boos. You feel shame.
Does anyone know how I can have it print the output prior to the prompt than wait for an input of y/n? Note I also need to be able to store the output produced by the command so "os.system()" won't work...
EDIT:
It looks like inside jirashell there is a part of the code that is waiting for an input and this is causing the block. Until something is passed into this input nothing is outputted... Still looking into how I can get around this. I'm in the process of trying to move the portion of code I need into my application. This solution doesn't seem elegant but I can't see any other way right now.
approved = input(
'Have you authorized this program to connect on your behalf to {}? (y/n)'.format(server))
Method which prints and caches the standard output:
You can use a thread which reads the standard output of your subprocess, while the main thread is blocked until the subprocess is done. The following example will run the program other.py, which looks like
#!/usr/bin/env python3
print("Hello")
x = input("Type 'yes': ")
Example:
import threading
import subprocess
import sys
class LivePrinter(threading.Thread):
"""
Thread which reads byte-by-byte from the input stream and writes it to the
standard out.
"""
def __init__(self, stream):
self.stream = stream
self.log = bytearray()
super().__init__()
def run(self):
while True:
# read one byte from the stream
buf = self.stream.read(1)
# break if end of file reached
if len(buf) == 0:
break
# save output to internal log
self.log.extend(buf)
# write and flush to main standard output
sys.stdout.buffer.write(buf)
sys.stdout.flush()
# create subprocess
p = subprocess.Popen('./other.py', stdout=subprocess.PIPE)
# Create reader and start the thread
r = LivePrinter(p.stdout)
r.start()
# Wait until subprocess is done
p.wait()
# Print summary
print(" -- The process is done now -- ")
print("The standard output was:")
print(r.log.decode("utf-8"))
The class LivePrinter reads every byte from the subprocess and writes it to the standard output. (I have to admit, this is not the most efficient approach, but a larger buffer size blocks, the LiveReader until the buffer is full, even though the subprocess is awaiting the answer to a prompt.) Since the bytes are written to sys.stdout.buffer, there shouldn't be a problem with multi-byte utf-8 characters.
The LiveReader class also stores the complete output of the subprocess in the variable log for later use.
As this answer summarizes, it is save to start a thread after forking with subprocess.
Original answer which has problems, when the prompt line doesn't end a line:
The output is delayed because communicate() blocks the execution of your script until the sub-process is done (https://docs.python.org/3/library/subprocess.html#subprocess.Popen.communicate).
You can read and print the standard output of the subprocess, while it is executed using stdout.readline. There are some issues about buffering, which require this rather complicated iter(process.stdout.readline, b'') construct. The following example uses gpg2 --gen-key because this command starts an interactive tool.
import subprocess
process = subprocess.Popen(["gpg2", "--gen-key"], stdout=subprocess.PIPE)
for stdout_line in iter(process.stdout.readline, b''):
print(stdout_line.rstrip())
Alternative answer which uses shell and does not cache the output:
As Sam pointed out, there is a problem with the above solution, when the prompt line does not end the line (which prompts they usually don't). An alternative solution is to use the shell argument to interact with the sub-process.
import subprocess
subprocess.call("gpg2 --gen-key", shell=True)
I'm writting a python script which receive several parameters on his standard input.
I use the raw_input() function which works for the firsts parameters but totally freeze when I call it inside a while loop.
Here is my code:
def launch_trade(logger):
Kerviel = Trader()
param = raw_input() #Works fine
Kerviel.Capital = float(param)
param = raw_input() #Works fine
Kerviel.NbDays = int(param)
param = raw_input() #Works Fine
while (param != '--end--'):
Kerviel.action(float(param), logger)
Kerviel.Cours.append(param)
param = raw_input() #Here it infinite wait
Actually this program works when I send all parameters myself in my console. But it is supposed to be called by a php script which sends it parameters on his stdin.
Why does this last raw_input() doesn't work when parameters are sent by a php script ?
Thanks for your answers and sorry for bad english.
Each input or raw_input call waits for a line ending with '\n'. A live user hits keys ending with the Enter key. A program has to send strings to stdout, connected to stdin, ending with '\n'. So the problem must be that the php program is not sending enough, or that not eveything it sends gets to your python program. If the latter, it could be that the php program needs to 'flush' stdout to push the last string to python. When a file is closed, flush is automatic, but stdout would not ordinarily be closed.
I got the following problem:
I have a Arduino here which is connected to my Mac.
The Arduino is reading and responding to serial data.
I made a little python3-script which takes user input and uses
pySerial to write commands like 'set(13, 255)~' to the Arduino.
That is just working fine:
while 1:
cmd = input("<<< ")
cmd += '~'
if cmd != "~":
serialPort.write(cmd.encode())
output = str(serialPort.readline()).upper()[2:-5]
if output != " ":
print(">>> ", output)
Here's the Problem:
If I want to send some "automated" commands within the script, e.g. in a loop, the Arduino is not responding anymore, even the commands send are
the same, also encoded and of type "byte".
for i in range[0, 256]:
cmd = 'set(13, '
cmd += str(i)
cmd += ')~'
serialPort.write(cmd.encode())
time.sleep(0.1)
The strange thing is, if i first declare the variable (cmd) as an input, its also working fine, but that means a manual interrupt every time the command is send.
cmd = input()
cmd = 'set(13, '
cmd += str(i)
cmd += ')~'
After some hours of research i absolutely have no idea what the problem might be. As I said, the datatype is always byte after encoding.
I don't know about PySerial, but I suspect it's because the write call don't send the data immediately.
When you use the input, it will add a newline to the buffer, which may trigger the write call to flush automatically.
In the other piece of code you have, you don't add a newline to the buffer and it won't flush the data, before the buffer is big enough.
So with a flush() call it should force send it.
Hope it makes sense
Well, I just had to make a longer interrupt after connecting to the port.
(Has not been a problem since I had to give some input so far)