How does Python handle Ctrl-C under Windows - python

Actually, there are two questions. However, I think they are closely related, so I ask them together. I use Python 2.7.10 32bit under Windows.
The first one is about this program:
import sys
sys.stdin.readline()
a = 1
b = 2
print 'hello'
When the program asks for input, if I press Ctrl-C, the program will be stopped by KeyboardInterrupt. However, this exception will happen in print 'hello', not in sys.stdin.readline().
The second one is about this program:
import sys
while True:
sys.stdin.readline()
When I want to stop this program, I have to first press Ctrl-C, then press Enter or press Ctrl-C again.
Neither of the problems happens under Linux, and they are annoying when debugging. I will be grateful if someone can give an detailed explanation.

Related

How to prevent user input into console when program is running in python? [duplicate]

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')

RPi - Python Curses program running at boot does not have keyboard focus

I am trying to write a program that will run when my raspberry pi starts up and will allow me to immediately begin typing things with my keyboard and have it be picked up by the program. I don't want to have to manually start the program when the pi starts. I need to use curses (or a similar unbuffered keyboard input library) because I display what I am typing on a 2x16 I2C LCD, but I also need everything that I am typing to be recorded to a text file.
Right now, I am auto-starting the program at boot by putting a line in rc.local. This works, and the I2C display is correctly showing program output, but it does not respond to keyboard input, and the keyboard input is instead displayed (when I connect the pie to a screen, the goal is to run headless) on an odd console layout that exits when I press enter and says -bash: 'whatever I just typed' command not found.
I have already tried:
Setting a timer at the beginning of the program to wait until the pi has fully booted before initializing the curses window and keyboard capture
Creating a seperate python program to wait until the pi has fully booted and then running the main script by importing it
Neither of these methods works though, I get the same problem with slight differences.
To be clear, the program works flawlessly if I run it manually from the command line. But there is no keyboard input to the program (or at least not where it is supposed to be inputted) when I autostart the script with rc.local.
My code:
#!/usr/bin/python
import I2C_LCD_driver, datetime, sys
from time import *
from subprocess import call
mylcd = I2C_LCD_driver.lcd()
for x in range(30): #waits for raspberry pi to boot up
mylcd.lcd_display_string("Booting Up: "+str(x), 1)
sleep(1)
import curses
key = curses.initscr()
curses.cbreak()
curses.noecho()
key.keypad(1)
key.nodelay(1)
escape=0
while escape==0:
#variable initialization
while 1:
k=key.getch()
if k>-1: #runs when you hit any key. getch() returns -1 until a key is pressed
if k==27: #exits the program when you hit Esc
break
elif k==269:
# a couple other special Function key cases are here
else:
inpt=chr(k)
mylcd.lcd_display_string(inpt,2,step) #writes the last character to the display
#some more code that handles writing the text to the LCD, which works flawlessly when run manually.
file.write("%s\r\n" % entry)
file.close()
mylcd.lcd_display_string("Saved ",2)
mylcd.lcd_display_string("F1 New F2 PwrOff",1)
while 1:
k=key.getch()
if k>-1:
if k==265: #do it again! with F1
mylcd.lcd_clear()
break
elif k==266: #shut down with F2
escape=1
break
curses.nocbreak()
key.keypad(0)
curses.echo()
curses.endwin()
call("sudo shutdown -h now", shell=True)
The line that I have in /etc/rc.local is as follows if that is important:
sudo python3 journal.py &
and it is followed by the 'exit 0' line.
Thanks for any help you can provide. I know this is a very specific problem and will be tedious to reproduce, but if anyone knows anything about autostarting functions I would be very appreciative of any tips.
Ok, literally all I had to do (which I did find after some more research on stackexchange, this is the thread that contained the answer I was looking for) was run my program from ~/.bashrc instead of /etc/rc.local. This method works perfectly, exactly what I wanted.
This should be because of how you called the program:
python3 journal.py &
You may want to check out JOB CONTROL of bash (or your shell) man page:
Only foreground
processes are allowed to read from ... the terminal. Background processes which
attempt to read from ... the
terminal are sent a SIGTTIN ... signal by the kernel's terminal
driver, which, unless caught, suspends the process.
In short, once curses (or anything for that matter) try to read from stdin your process is likely stopped (after it may have already written to your display). Keep it in the foreground to be able to have it use stdin (and by extension keyboard).
Side note: Not sure about distro and details of implementation of rc.local in your case, but aren't init scripts normally run with uid/gid 0 already (without wrapping individual calls through sudo?)

Why doesn't this Python keyboard interrupt work? (in PyCharm)

My Python try/except loop does not seem to trigger a keyboard interrupt when Ctrl + C is pressed while debugging my code in PyCharm. (The same issue occurs when using Ctrl + C while running the program, but not in the PyCharm Python console.)
My code look like this:
try:
while loop:
print("busy")
except KeyboardInterrupt:
exit()
The full code can be viewed here. The code above produces the same error.
I know this is an old question, but I ran into the same problem and think there's an easier solution:
In PyCharm go to "Run"/"Edit Configurations" and check "Emulate terminal in output console".
PyCharm now accepts keyboard interrupts (make sure the console is focused).
Tested on:
PyCharm 2019.1 (Community Edition)
From your screen shot it appears that you are running this code in an IDE. The thing about IDEs is that they are not quite the same as running normally, especially when it comes to handling of keyboard characters. The way you press ctrl-c, your IDE thinks you want to copy text. The python program never sees the character. Pehaps it brings up a separate window when running? Then you would select that window before ctrl-c.
PyCharm's Python Console raises the exception console_thrift.KeyboardInterruptException on Ctrl-C instead of KeyboardInterrupt. The exception console_thrift.KeyboardInterruptException is not a subclass of KeyboardInterrupt, therefore not caught by the line except KeyboardInterrupt.
Adding the following lines would make your script compatible with PyCharm.
try:
from console_thrift import KeyboardInterruptException as KeyboardInterrupt
except ImportError:
pass
This would not break compatibility with running the script in a terminal, or other IDE, like IDLE or Spyder, since the module console_thrift is found only within PyCharm.
If that comment doesn't solve your problem, (from #tdelaney) you need to have your shell window focused (meaning you've clicked on it when the program is running.) and then you can use Control+C
You can also use PyCharm's Python console and use Ctrl + C, if you catch the exception that PyCharm raises when Ctrl + C is pressed. I wrote a short function below called is_keyboard_interrupt that tells you whether the exception is KeyboardInterrupt, including PyCharm's. If it is not, simply re-raise it. I paste a simplified version of the code below.
When it is run:
type 'help' and press Enter to repeat the loop.
type anything else and press Enter to check that ValueError is handled properly.
Press Ctrl + C to check that KeyboardInterrupt is caught, including in PyCharm's python console.
Note: This doesn't work with PyCharm's debugger console (the one invoked by "Debug" rather than "Run"), but there the need for Ctrl + C is less because you can simply press the pause button.
I also put this on my Gist where I may make updates: https://gist.github.com/yulkang/14da861b271576a9eb1fa0f905351b97
def is_keyboard_interrupt(exception):
# The second condition is necessary for it to work with the stop button
# in PyCharm Python console.
return (type(exception) is KeyboardInterrupt
or type(exception).__name__ == 'KeyboardInterruptException')
try:
def print_help():
print("To exit type exit or Ctrl + c can be used at any time")
print_help()
while True:
task = input("What do you want to do? Type \"help\" for help:- ")
if task == 'help':
print_help()
else:
print("Invalid input.")
# to check that ValueError is handled separately
raise ValueError()
except Exception as ex:
try:
# Catch all exceptions and test if it is KeyboardInterrupt, native or
# PyCharm's.
if not is_keyboard_interrupt(ex):
raise ex
print('KeyboardInterrupt caught as expected.')
print('Exception type: %s' % type(ex).__name__)
exit()
except ValueError:
print('ValueError!')
Here is working normally, since i put a variable "x" in your code and i use tabs instead spaces.
try:
def help():
print("Help.")
def doStuff():
print("Doing Stuff")
while True:
x = int(input())
if x == 1:
help()
elif x == 2:
doStuff()
else:
exit()
except KeyboardInterrupt:
exit()
Try shift + control + C. It worked for me.
Make sure the window is selected when you press ctrl+c. I just ran your program in IDLE and it worked perfectly for me.
One possible reason if <Strg+C> does not stop the program:
When a text is marked in the shell, <Strg+C> is interpreted as "copy the marked text to clipboard".
Just unmark the text and press <Strg+C> again.

Python time.sleep in Spyder: how to pause between print statements?

Edit: the linked post does not solve this problem, this is still an outstanding issue.
I execute the following program in Spyder with iPython:
import time
print "Please enter your name."
userName=raw_input();
print "Now let's wait a few seconds, {}.".format(userName)
time.sleep(1)
print "Did you lose your patience?"
It prompts the user for their name, then (instead of printing the first line, waiting, and then printing the second line), it pauses and then prints the outputs of the last two print statements at the same time.
When I run from the command line, it works as expected. So does anyone know what I can do so that the script shows the desired behavior from within Spyder/iPython (I am working in Windows 7, Spyder 2.2.5 running iPython with Python 2.7).
Note because I get the expected behavior when I run from the command line, the suggestion at Why is time.sleep pausing early? is not transparently applicable, but perhaps it is easy to port that solution to this case? Also, running 'sys.stdout.flush()' before the sleep command doesn't seem to do anything.
I tested this in Spyder and vanza's solution works for me. Are you sure you put sys.stdout.flush() in the right line? It should look like this:
vanza's solution:
import time
import sys
print "Please enter your name."
userName=raw_input();
print "Now let's wait a few seconds, {}.".format(userName)
sys.stdout.flush() # <- *** it goes here ***
time.sleep(3)
print "Did you lose your patience?"
You might want to scroll down the iPython console a bit before entering your input, to see the text clearly (otherwise it stays a bit low to be noticed).

Python kbhit() problems

I'm trying to write a very simple program that will wait for x seconds before checking to see it a key has been pressed then, depending on this outcome will go into a different loop further down the code. I have this code:
import msvcrt
import time
import sys
time.sleep(1)
if msvcrt.kbhit():
sys.stdout.write('y')
else:
sys.stdout.write('n')
So I press any key when it first starts (making kbhit ==true) but it always just falls to the second statement and prints 'n'.
Any suggestions what I'm doing wrong?
{Using Python 2.7 and IDLE}
Thanks
The msvcrt.kbhit() function will only work if the program it is in has been run from the windows command line (or if a console window is opened for its input and output when you double click on its .py file).
If you run from IDLE or using the pythonw.exe interpreter, the program won't be connected to a console window and the console-IO commands from msvcrt won't work.

Categories

Resources