I want to create one program in which two lists of hosts are available. I want to read data from each host. It will take around 5-10 seconds so I want to read each host data with different thread.
I created below code and it is working as per my expectations but only problem is when I'm pressing Ctrl+c, program didn't terminate.
My code:
import threading
import time,os,sys
import signal
is_running = True
def signal_handler(signal, frame):
print "cleaning up...please wait..."
v1.stop()
v2.stop()
global is_running
is_running = False
class Thread2(threading.Thread):
def __init__(self, function,args):
self.running = False
self.function = function
self.args = args
super(Thread2, self).__init__()
def start(self):
self.running = True
super(Thread2, self).start()
def run(self):
while is_running:
self.function(self.args)
time.sleep(time_interval)
def stop(self):
self.running = False
def b_iterate(hostnames):
for host_name in hostnames:
v = Thread2(function = read_cet_data,args = host_name)
v.start()
def read_b_data(host):
#
#reading some data from current host (5-10 seconds processing)
#
#here, this thread is not neccessary, want to stop or kill or terminate it
if threading.current_thread().isAlive():
threading.current_thread().stop()
def a_iterate(entp_hostnames):
for host_name in entp_hostnames:
v = Thread2(function = read_entp_data,args = host_name)
v.start()
def read_a_data(host):
#
#reading some data from current host (5-10 seconds processing)
#
#here, this thread is not neccessary, want to stop or kill or terminate it
if threading.current_thread().isAlive():
threading.current_thread().stop()
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
#a_hostnmaes & b_hostnmaes are the lists of hostnames
v1 = Thread2(function = a_iterate,args = a_hostnames)
v2 = Thread2(function = b_iterate,args = b_hostnames)
v1.start()
v2.start()
while is_running:
pass
How I can make this program terminate after pressing Ctrl+c. Am I missing something?
If you just want control C to finish everything, there is no need to use a stop function in threads. You can just daemonise them:
v1 = Thread2(function = a_iterate,args = a_hostnames)
v2 = Thread2(function = b_iterate,args = b_hostnames)
v1.daemon = True
v2.daemon = True
v1.start()
v2.start()
As soon as your main program dies, these threads die as well. You need to add .daemon = True to all other locations in the code where a thread is created.
Hannu
You can either
catch KeyboardInterrupt in main thread
set a flag so another threads can detect it and exit
or
catch KeyboardInterrupt
call os._exit()
I have a program like this:
from threading import Thread
def foo1(arg):
print("foo1 >>> Something")
input("foo1 >>> Enter Something")
...
def foo2(arg):
print("foo2 >>> Something")
input("foo2 >>> Enter Something")
...
def main():
th1 = Thread(target= foo1)
th1.start()
th2 = Thread(target= foo2)
th2.start()
This program runs both the functions(foo1 and foo2) in the same terminal window. Can I in some way run them in a different terminal window. What I don't wish is to re-run the program. The reason is that they print and take input at the same place and same time. I don't want. Any method?
What you are trying to accomplish isn't possible with just threads, when you create a new Thread it shares all the variables with other threads in your program, including sys.stdout / sys.stdin.
Normally you don't have to worry about PIPES in python programs because it takes care of it for you. print sends the text to sys.stdout and input grabs text from sys.stdin (and error messages are sent to sys.stderr)
So running one program in two terminal windows would mean you would have to have more then one input/output streams, to which there are two solutions:
run a completely separate program with subprocess.Popen like the other fellow described and figure out how to bridge information across the two which is a real pain.
or 2. create your own terminal window with something like tkinter, which is difficult from scratch but luckily IDLE has the majority of the code available in the standard library.
Here is an adapted version of PyShell from idlelib.PyShell to run a Thread instead of the interactive interpretive:
from idlelib import PyShell,EditorWindow
import threading,sys
try:
import tkinter as tk #python 3.X
except ImportError:
import Tkinter as tk #python 2
import tkMessageBox as messagebox
tk.messagebox = messagebox
class ThreadShell(PyShell.PyShell):
"""mostly copied from idlelib.PyShell module but adapted to work with threads"""
#__adapted_by__ = "Tadhg McDonald-Jensen"
def __init__(self, tk_root,target=None):
#not sure exactly what the FileList object is for but it is required by the shell
flist = PyShell.PyShellFileList(tk_root)
super(ThreadShell,self).__init__(flist)
#internal event flag for input, allows thread waiting for input to wait until a tk event handles it
self.__input_flag = threading.Event()
#target is stored and called in .run_command() which also deals with finishing the shell
self.target = target
self.thread = threading.Thread(target=self.run_command)
#tk_root.after makes the .start method call when the program starts (after 0 miliseconds)
tk_root.after(0,self.start)
def start(self):
"""starts executing the Thread"""
super(ThreadShell,self).beginexecuting()
try:
self.thread.start()
except RuntimeError:
self.executing = 0
self.canceled = 0
#self.top.quit() #this causes double deletion warnings with better Implementation of mainloop
beginexecuting = start
def run_command(self):
"""calls target from constructor with self as argument then cleans up shell"""
if self.target:
self.target(self)
self.prompt_exit()
self.executing = 0
self.canceled = 0
try:
self.text.after(1,self.close)
except RuntimeError:
pass #tkinter has issues with changing threads so often after closing one shell others will throw this error
def printf(self,*stuff,**kw):
"""works just like python 3.x print function but writes to shell's .stdout file"""
if self.executing:
## if USING_OLD_METHOD: #Pretty sure this would do exact same thing
## kw.setdefault("file",self.stdout)
## print(*stuff,**kw), self.resetoutput()
## return
sep = kw.get("sep"," ")
end = kw.get("end","\n")
text = sep.join(stuff) + end
self.stdout.write(text)
self.resetoutput()
def input(self,prompt="",timeout=None):
"""python 2 equivelent to raw_input or py 3+ input
Prompts user for input and freezes thread until input is given
Will return "" if .executing is False or it timed out from optional timeout argument"""
if self.executing or self.closing:
if prompt:
self.stdout.write(prompt)
self.__in_buffer = ""
self.__input_flag.clear()
self.reading=True
self.__input_flag.wait(timeout)
#input is inserted into .__in_buffer by other events
#then set __input_flag so that it can be delivered to thread
self.reading = False
return self.__in_buffer.strip("\n")
else:
raise RuntimeError("cannot take input after finished")
def prompt_exit(self):
"""writes press enter to quit" to the console colour then waits for input"""
self.executing = False
self.closing = True
self.console.write("\n press enter to quit")
self.input()
def join_thread(self,timeout=None):
"""sets .executing label to False then waits to join thead,
returns True if thread finished or False if timeout activated"""
self.executing = False
self.closing = True
if self.thread:
self.thread.join(timeout)
return not self.thread.is_alive()
def _close(self):
"Extend EditorWindow._close(), joins thread to close it"
# Restore std streams
sys.stdout = self.save_stdout
sys.stderr = self.save_stderr
sys.stdin = self.save_stdin
# Break cycles
self.interp = None
self.console = None
self.flist.pyshell = None
self.history = None
EditorWindow.EditorWindow._close(self)
self.join_thread()
def stop_readline(self):
self.__in_buffer = ""
self.__input_flag.set()
def update_in(self):
"""updates input from user, I think some of the labels are probably unnecessary but it is easier to leave it alone"""
line = self.text.get("iomark", "end-1c")
if len(line) == 0: # may be EOF if we quit our mainloop with Ctrl-C
line = "\n"
self.resetoutput()
if self.canceled:
self.canceled = 0
if self.endoffile:
self.endoffile = 0
line = ""
self.__in_buffer = line
self.__input_flag.set()
def cancel_callback(self, event=None):
try:
if self.text.compare("sel.first", "!=", "sel.last"):
return # Active selection -- always use default binding
except:
pass
if not (self.executing or self.reading):
return "break"
self.endoffile = 0
self.canceled = 1
if self.reading:
self.update_in()
return "break"
def eof_callback(self, event):
if self.executing and not self.reading:
return # Let the default binding (delete next char) take over
if not (self.text.compare("iomark", "==", "insert") and
self.text.compare("insert", "==", "end-1c")):
return # Let the default binding (delete next char) take over
if not self.executing:
self.resetoutput()
self.close()
else:
self.canceled = 0
self.endoffile = 1
self.update_in()
return "break"
def enter_callback(self, event):
"""called when the enter/return key is pressed,
only the recursive self.top.mainloop() / self.top.quit() had to be changed for support"""
# it is very long to copy/paste for the one line change, so I override the method temporarily
save = self.top.quit
self.top.quit = self.update_in
super(ThreadShell,self).enter_callback(event)
self.top.quit = save
#stupid module depends on this being set from the main function, so it needs to be done manually
PyShell.use_subprocess = True
#this defines the root tkinter window and sets it up
root = tk.Tk()
EditorWindow.fixwordbreaks(root)
root.withdraw()
#I need this to work on my mac, not sure if there are other OS specific stuff that should be included
try:
from idlelib import macosxSupport
macosxSupport.setupApp(root, None)
except (ImportError,AttributeError):
pass
##!!!!!!!!!!!!!!!!!!!! And This Is The Part You Need To Worry About !!!!!!!!!!!!!!!!!!!!##
switch = threading.Event()
switch.clear()
def foo(shell):
global x
x = shell.input("enter a message: ")
switch.set()
shell.printf("message sent")
def foo2(shell):
shell.printf("waiting for message...")
while shell.executing and not switch.is_set():
switch.wait(2) # by using shell.executing in the loop it will occasionally check
# if the program should quit because the window was closed
if shell.executing:
shell.printf("message recieved: ",x)
shell1 = ThreadShell(root,foo)
shell2 = ThreadShell(root,foo2)
first_time = True
while shell1.executing or shell2.executing or first_time:
first_time = False
root.mainloop()
root.destroy()
#!/usr/bin/env python
"""Show messages in two new console windows simultaneously."""
import sys
import platform
from subprocess import Popen
messages = 'This is Console1', 'This is Console2'
def randomFunction():
return "import sys; print(sys.argv[1]); input('Press Enter..')"
# define a command that starts new terminal
if platform.system() == "Windows":
new_window_command = "cmd.exe /c start".split()
else: #XXX this can be made more portable
new_window_command = "x-terminal-emulator -e".split()
# open new consoles, display messages
echo = [sys.executable, "-c",randomFunction()
]
processes = [Popen(new_window_command + echo + [msg]) for msg in messages]
# wait for the windows to be closed
for proc in processes:
proc.wait()
Find working solution for your problem, I haven't used thread, but can be done. And this solution is motivated from solution provided by "Miodrag Novakovic"
You have to change few paths as per your env. Below code is tested on windows
test_code.py -
import sys
import platform
from subprocess import Popen
messages = 'This is Console1', 'This is Console2'
def foo1():
print "In foo1"
i = input("Enter Something - ")
print i
input("Enter to exit")
def foo2():
print "In foo2"
i = input("Enter Something - ")
print i
input("Enter to exit")
def run_foo1():
print("foo1 >>> Something")
return "import sys; sys.path.append('path_to_your_program_folder'); from test_code import foo1; foo1()"
def run_foo2():
print("foo2 >>> Something")
return "import sys; sys.path.append('path_to_your_program_folder'); from test_code import foo2; foo2()"
# define a command that starts new terminal
if platform.system() == "Windows":
new_window_command = "cmd.exe /c start".split()
else: #XXX this can be made more portable
new_window_command = "x-terminal-emulator -e".split()
if __name__ == '__main__':
# open new consoles, display messages
echos = [[sys.executable, "-c",run_foo1()],
[sys.executable, "-c",run_foo2()]
]
processes = [Popen(new_window_command + echo) for echo in echos]
# wait for the windows to be closed
for proc in processes:
proc.wait()
I am trying to make a simple IRC client in Python (as kind of a project while I learn the language).
I have a loop that I use to receive and parse what the IRC server sends me, but if I use raw_input to input stuff, it stops the loop dead in its tracks until I input something (obviously).
How can I input something without the loop stopping?
(I don't think I need to post the code, I just want to input something without the while 1: loop stopping.)
I'm on Windows.
For Windows, console only, use the msvcrt module:
import msvcrt
num = 0
done = False
while not done:
print(num)
num += 1
if msvcrt.kbhit():
print "you pressed",msvcrt.getch(),"so now i will quit"
done = True
For Linux, this article describes the following solution, it requires the termios module:
import sys
import select
import tty
import termios
def isData():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
old_settings = termios.tcgetattr(sys.stdin)
try:
tty.setcbreak(sys.stdin.fileno())
i = 0
while 1:
print(i)
i += 1
if isData():
c = sys.stdin.read(1)
if c == '\x1b': # x1b is ESC
break
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
For cross platform, or in case you want a GUI as well, you can use Pygame:
import pygame
from pygame.locals import *
def display(str):
text = font.render(str, True, (255, 255, 255), (159, 182, 205))
textRect = text.get_rect()
textRect.centerx = screen.get_rect().centerx
textRect.centery = screen.get_rect().centery
screen.blit(text, textRect)
pygame.display.update()
pygame.init()
screen = pygame.display.set_mode( (640,480) )
pygame.display.set_caption('Python numbers')
screen.fill((159, 182, 205))
font = pygame.font.Font(None, 17)
num = 0
done = False
while not done:
display( str(num) )
num += 1
pygame.event.pump()
keys = pygame.key.get_pressed()
if keys[K_ESCAPE]:
done = True
This is the most awesome solution1 I've ever seen. Pasted here in case link goes down:
#!/usr/bin/env python
'''
A Python class implementing KBHIT, the standard keyboard-interrupt poller.
Works transparently on Windows and Posix (Linux, Mac OS X). Doesn't work
with IDLE.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
'''
import os
# Windows
if os.name == 'nt':
import msvcrt
# Posix (Linux, OS X)
else:
import sys
import termios
import atexit
from select import select
class KBHit:
def __init__(self):
'''Creates a KBHit object that you can call to do various keyboard things.
'''
if os.name == 'nt':
pass
else:
# Save the terminal settings
self.fd = sys.stdin.fileno()
self.new_term = termios.tcgetattr(self.fd)
self.old_term = termios.tcgetattr(self.fd)
# New terminal setting unbuffered
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
# Support normal-terminal reset at exit
atexit.register(self.set_normal_term)
def set_normal_term(self):
''' Resets to normal terminal. On Windows this is a no-op.
'''
if os.name == 'nt':
pass
else:
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
def getch(self):
''' Returns a keyboard character after kbhit() has been called.
Should not be called in the same program as getarrow().
'''
s = ''
if os.name == 'nt':
return msvcrt.getch().decode('utf-8')
else:
return sys.stdin.read(1)
def getarrow(self):
''' Returns an arrow-key code after kbhit() has been called. Codes are
0 : up
1 : right
2 : down
3 : left
Should not be called in the same program as getch().
'''
if os.name == 'nt':
msvcrt.getch() # skip 0xE0
c = msvcrt.getch()
vals = [72, 77, 80, 75]
else:
c = sys.stdin.read(3)[2]
vals = [65, 67, 66, 68]
return vals.index(ord(c.decode('utf-8')))
def kbhit(self):
''' Returns True if keyboard character was hit, False otherwise.
'''
if os.name == 'nt':
return msvcrt.kbhit()
else:
dr,dw,de = select([sys.stdin], [], [], 0)
return dr != []
# Test
if __name__ == "__main__":
kb = KBHit()
print('Hit any key, or ESC to exit')
while True:
if kb.kbhit():
c = kb.getch()
if ord(c) == 27: # ESC
break
print(c)
kb.set_normal_term()
1
Made by Simon D. Levy, part of a compilation of software he has written and released under the Gnu Lesser General Public License.
Here a solution that runs under linux and windows using a seperate thread:
import sys
import threading
import time
import Queue
def add_input(input_queue):
while True:
input_queue.put(sys.stdin.read(1))
def foobar():
input_queue = Queue.Queue()
input_thread = threading.Thread(target=add_input, args=(input_queue,))
input_thread.daemon = True
input_thread.start()
last_update = time.time()
while True:
if time.time()-last_update>0.5:
sys.stdout.write(".")
last_update = time.time()
if not input_queue.empty():
print "\ninput:", input_queue.get()
foobar()
My favorite to get non-blocking input is using the python input() in a thread:
import threading
class KeyboardThread(threading.Thread):
def __init__(self, input_cbk = None, name='keyboard-input-thread'):
self.input_cbk = input_cbk
super(KeyboardThread, self).__init__(name=name)
self.start()
def run(self):
while True:
self.input_cbk(input()) #waits to get input + Return
showcounter = 0 #something to demonstrate the change
def my_callback(inp):
#evaluate the keyboard input
print('You Entered:', inp, ' Counter is at:', showcounter)
#start the Keyboard thread
kthread = KeyboardThread(my_callback)
while True:
#the normal program executes without blocking. here just counting up
showcounter += 1
OS independent, only internal libraries, supports multi-character input
On Linux, here's a refactoring of mizipzor's code that makes this a little easier, in case you have to use this code in multiple places.
import sys
import select
import tty
import termios
class NonBlockingConsole(object):
def __enter__(self):
self.old_settings = termios.tcgetattr(sys.stdin)
tty.setcbreak(sys.stdin.fileno())
return self
def __exit__(self, type, value, traceback):
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings)
def get_data(self):
if select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], []):
return sys.stdin.read(1)
return False
Here's how to use this: This code will print a counter that keeps growing until you press ESC.
with NonBlockingConsole() as nbc:
i = 0
while 1:
print i
i += 1
if nbc.get_data() == '\x1b': # x1b is ESC
break
I think curses library can help.
import curses
import datetime
stdscr = curses.initscr()
curses.noecho()
stdscr.nodelay(1) # set getch() non-blocking
stdscr.addstr(0,0,"Press \"p\" to show count, \"q\" to exit...")
line = 1
try:
while 1:
c = stdscr.getch()
if c == ord('p'):
stdscr.addstr(line,0,"Some text here")
line += 1
elif c == ord('q'): break
"""
Do more things
"""
finally:
curses.endwin()
.... backing to the initial question ...
i am learning python too, it cost me many documentation and examples readings and head crackings... but i think i reached an easy, simple, short and compatible solution... using just input, lists and threads
'''
what i thought:
- input() in another thread
- that were filling a global strings list
- strings are being popped in the main thread
'''
import threading
consoleBuffer = []
def consoleInput(myBuffer):
while True:
myBuffer.append(input())
threading.Thread(target=consoleInput, args=(consoleBuffer,), daemon=True).start() # start the thread
import time # just to demonstrate non blocking parallel processing
while True:
time.sleep(2) # avoid 100% cpu
print(time.time()) # just to demonstrate non blocking parallel processing
while consoleBuffer:
print(repr(consoleBuffer.pop(0)))
until this is the simplest and compatible way i found, be aware by default stdin stdout and stderr share the same terminal so "local echo" of your input may look inconsistent if something is printed on console while you are typing, however after pressing enter the typed string is received well... if you don't want/like this behavior find a way to separate input/output areas like redirections, or try another solution like curses, tkinter, pygame, etc.
BONUS: the ctrl-c keystroke can be easily handled with
try:
# do whatever
except KeyboardInterrupt:
print('cancelled by user') or exit() # overload
I'd do what Mickey Chan said, but I'd use unicurses instead of normal curses.
Unicurses is universal (works on all or at least almost all operating systems)
If you just want a single "escape" from a loop, you can intercept the Ctrl-C signal.
This is cross-platform and very simple!
import signal
import sys
def signal_handler(sig, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
while True:
# do your work here
With python3.3 and above you can use the asyncio module as mentioned in this answer.
You will have to re factor your code though to work with asyncio.
Prompt for user input using python asyncio.create_server instance
Since I found one of the answers above helpful, here's an example of a similar approach. This code creates a metronome effect while taking input.
The difference is this code uses a closure instead of a class, which feels a little more straight-forward to me. This example also incorporates a flag to kill the thread via my_thread.stop = True, but without using a global variable. I do this by (ab)using the fact that python functions are objects and thus can be monkey-patched, even from inside themselves.
Note: Stopping threads should be done with caution. If your thread has data that needs some kind of clean up process or if the thread spawned its own threads, this approach will unceremoniously kill those processes.
# Begin metronome sound while accepting input.
# After pressing enter, turn off the metronome sound.
# Press enter again to restart the process.
import threading
import time
import winsound # Only on Windows
beat_length = 1 # Metronome speed
def beat_thread():
beat_thread.stop = False # Monkey-patched flag
frequency, duration = 2500, 10
def run(): # Closure
while not beat_thread.stop: # Run until flag is True
winsound.Beep(frequency, duration)
time.sleep(beat_length - duration/1000)
threading.Thread(target=run).start()
while True:
beat_thread()
input("Input with metronome. Enter to finish.\n")
beat_thread.stop = True # Flip monkey-patched flag
input("Metronome paused. Enter to continue.\n\n")
The following is an class wrapper around one of the above solutions:
#!/usr/bin/env python3
import threading
import queue
class NonBlockingInput:
def __init__(self, exit_condition):
self.exit_condition = exit_condition
self.input_queue = queue.Queue()
self.input_thread = threading.Thread(target=self.read_kbd_input, args=(), daemon=True)
self.input_thread.start()
def read_kbd_input(self):
done_queueing_input = False
while not done_queueing_input:
console_input = input()
self.input_queue.put(console_input)
if console_input.strip() == self.exit_condition:
done_queueing_input = True
def input_queued(self):
return_value = False
if self.input_queue.qsize() > 0:
return_value = True
return return_value
def input_get(self):
return_value = ""
if self.input_queue.qsize() > 0:
return_value = self.input_queue.get()
return return_value
if __name__ == '__main__':
NON_BLOCK_INPUT = NonBlockingInput(exit_condition='quit')
DONE_PROCESSING = False
INPUT_STR = ""
while not DONE_PROCESSING:
if NON_BLOCK_INPUT.input_queued():
INPUT_STR = NON_BLOCK_INPUT.input_get()
if INPUT_STR.strip() == "quit":
DONE_PROCESSING = True
else:
print("{}".format(INPUT_STR))
I was writing a program using Linux that has a bigger mainloop that requires regular updates but also needs to read characters in a non-blocking way. But resetting the display, also loses the input buffer.
This is the solution that I came up with. Every time after the screen is updated it sets the terminal to non-blocking, waits for the mainloop to pass and then interprets stdin.
After that the terminal gets reset to the original settings.
#!/usr/bin/python3
import sys, select, os, tty, termios, time
i = 0
l = True
oldtty = termios.tcgetattr(sys.stdin)
stdin_no = sys.stdin.fileno()
while l:
os.system('clear')
print("I'm doing stuff. Press a 'q' to stop me!")
print(i)
tty.setcbreak(stdin_no)
time.sleep(0.5)
if sys.stdin in select.select([sys.stdin], [], [], 0.0)[0]:
line = sys.stdin.read(1)
print (line, len(line))
if "q" in line:
l = False
else:
pass
termios.tcsetattr(stdin_no, termios.TCSADRAIN, oldtty)
i += 1
The Solution by marco is the right idea, but I decided to simplify it to the minimal possible code without any classes. Also it actually shows you how to get the user input with the queue library instead of just printing it:
import time, threading, queue
def collect(que):
msg = input()
que.put(msg)
que = queue.Queue()
thread = threading.Thread(target=collect, args=[que])
thread.start()
while thread.is_alive():
time.sleep(1)
print("The main thread continues while we wait for you...")
msg = que.get()
print('You typed:', msg)
In this example, the main thread continues indefinitely (processing data or whatever), while periodically checking to see if the user has input any data in the spawned thread. When that happens it returns the user input.
I've successfully used this idea in my own script to create a debugger, where I can type "print variable name" at any point during the main loop and it gives me the values in real time without stopping.
My example below does allow for non-blocking reads from stdin under both Windows (only tested under Windows 10) and Linux without requiring external dependencies or using threading. It works for copypasted text, it disables ECHO, so it could be used for e.g. some sort of custom UI and uses a loop, so it would be easy to process anything that was input into it.
With the above in mind, the example is meant for an interactive TTY, not piped input.
#!/usr/bin/env python3
import sys
if(sys.platform == "win32"):
import msvcrt
import ctypes
from ctypes import wintypes
kernel32 = ctypes.windll.kernel32
oldStdinMode = ctypes.wintypes.DWORD()
# Windows standard handle -10 refers to stdin
kernel32.GetConsoleMode(kernel32.GetStdHandle(-10), ctypes.byref(oldStdinMode))
# Disable ECHO and line-mode
# https://learn.microsoft.com/en-us/windows/console/setconsolemode
kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), 0)
else:
# POSIX uses termios
import select, termios, tty
oldStdinMode = termios.tcgetattr(sys.stdin)
_ = termios.tcgetattr(sys.stdin)
# Disable ECHO and line-mode
_[3] = _[3] & ~(termios.ECHO | termios.ICANON)
# Don't block on stdin.read()
_[6][termios.VMIN] = 0
_[6][termios.VTIME] = 0
termios.tcsetattr(sys.stdin, termios.TCSAFLUSH, _)
def readStdin():
if(sys.platform == "win32"):
return msvcrt.getwch() if(msvcrt.kbhit()) else ""
else:
return sys.stdin.read(1)
def flushStdin():
if(sys.platform == "win32"):
kernel32.FlushConsoleInputBuffer(kernel32.GetStdHandle(-10))
else:
termios.tcflush(sys.stdin, termios.TCIFLUSH)
try:
userInput = ""
print("Type something: ", end = "", flush = True)
flushStdin()
while 1:
peek = readStdin()
if(len(peek) > 0):
# Stop input on NUL, Ctrl+C, ESC, carriage return, newline, backspace, EOF, EOT
if(peek not in ["\0", "\3", "\x1b", "\r", "\n", "\b", "\x1a", "\4"]):
userInput += peek
# This is just to show the user what they typed.
# Can be skipped, if one doesn't need this.
sys.stdout.write(peek)
sys.stdout.flush()
else:
break
flushStdin()
print(f"\nuserInput length: {len(userInput)}, contents: \"{userInput}\"")
finally:
if(sys.platform == "win32"):
kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), oldStdinMode)
else:
termios.tcsetattr(sys.stdin, termios.TCSAFLUSH, oldStdinMode)
I have a question. I'd like to send a continuous streams of byte to some host for certain amount of time (let's say 1 minute) using python.
Here is my code so far:
#! /usr/bin/env python
import socket
import thread
import time
IP = "192.168.0.2"
PADDING = "a" * 1000 #assume the MTU is slighly above 1000
DATA = PADDING + "this is sentence number = "
PORT = 14444
killed = False
test_time = 60 #60 seconds of testing
def send_data():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
count = 1
starttime = time.clock()
while elapsed < test_time:
sent = s.send(DATA + str(count) + "\n")
if sent == 0: break # assume that if nothing is sent -> connection died
count = count+1
elapsed = time.clock() - starttime
if killed:
break
s.close()
print str(count) + " has been sent"
print "to quit type quit"
thread.start_new_thread(send_data, ())
while True:
var = raw_input("Enter something: ")
if var == "quit":
killed = True
Few question, is there a better way to let a thread die after 60 seconds other than polling the time.clock every time?
When I run this program, it sends the bytes correctly but when I typed quit the other thread won't die, even though I set the var killed = True. I wonder why is that? the scope of var Killed should reach the other thread right?
Thanks
I recommned using threading module. Even more benefit is to use InterruptableThread for terminating the thread. You do not have to use flag for terminating your thread but exception will occur if you call terminate() on this thread from parent. You can handle exception or not.
import threading, ctypes
class InterruptableThread(threading.Thread):
#classmethod
def _async_raise(cls, tid, excobj):
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(excobj))
if res == 0:
raise ValueError("nonexistent thread id")
elif res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
raise SystemError("PyThreadState_SetAsyncExc failed")
def raise_exc(self, excobj):
assert self.isAlive(), "thread must be started"
for tid, tobj in threading._active.items():
if tobj is self:
self._async_raise(tid, excobj)
return
def terminate(self):
self.raise_exc(SystemExit)
EDIT:
You can rewrite your code like this using another thread that is waiting 1 minute and then killing your other thread
def send_data:
IP = ...
# other vars
...
s = socket.socket(.....)
# no killed checking
# no time checking
# just do your work here
...
s.close()
my_thread = InterruptableThread(target=send_data)
my_thread.start()
def one_minute_kill(who):
time.sleep(60)
who.terminate()
killer_thread = InterruptableThread(target=one_minute_kill, args=[my_thread])
killer.start()
print "to quit type quit"
while my_thread.isAlive():
if raw_input("Enter something: ") == "quit":
my_thread.terminate()
I don't know how to do this with the "thread" module, but I can do it with the "threading" module. I think this code accomplishes what you want.
For documentation on the threading module:
http://docs.python.org/library/threading.html
#!/usr/bin/python
import time
from threading import Thread
import threading
import sys
test_time = 10
killed = False
class SillyThread( threading.Thread ):
def run(self):
global killed
starttime = time.time()
counter = 0
while (time.time() - starttime) < test_time:
if killed:
break
counter = counter + 1
time.sleep(0.1)
print "I did %d loops" % counter
class ManageThread( threading.Thread ):
def run(self):
global killed
while True:
var = raw_input("Enter something: ")
if var == "quit":
killed = True
break
print "Got var [%s]" % var
silly = SillyThread()
silly.start()
ManageThread().start()
Thread.join(silly)
print "bye bye"
sys.exit(0)
Note that I use time.time() instead of time.clock(). time.clock() gives elapsed processor time on Unix (see http://docs.python.org/library/time.html). I think time.clock() should work everywhere. I set my test_time to 10 seconds because I don't have the patience for a minute.
Here's what happens if I let it run the full 10 seconds:
leif#peacock:~/tmp$ ./test.py
Enter something: I did 100 loops
bye bye
Here's what happens if I type 'quit':
leif#peacock:~/tmp$ ./test.py
Enter something: quit
Got var [quit]
I did 10 loops
bye bye
Hope this helps.
As mentioned above, use the threading module, it is much easier to use and provides several synchronization primitives. It also provides a Timer class that runs after a specified amount of time.
If you just want the program to exit, you can simply make the sending thread a daemon. You do this by calling setDaemon(True) before calling start() (2.6 might use a daemon attribute instead). Python won't exit so long as a non-daemon thread is running.
You can do this pretty easily without threads. For example, using Twisted, you just set up a timed call and a producer:
from twisted.internet.protocol import ClientFactory, Protocol
from twisted.internet import reactor
class Noisy(Protocol):
def __init__(self, delay, data):
self.delay = delay
self.data = data
def stop(self):
self.transport.unregisterProducer()
self.transport.loseConnection()
reactor.stop()
def resumeProducing(self):
self.transport.write(self.data)
def connectionMade(self):
self.transport.registerProducer(self, False)
reactor.callLater(self.delay, self.stop)
factory = ClientFactory()
factory.protocol = lambda: Noisy(60, "hello server")
reactor.connectTCP(host, port, factory)
reactor.run()
This has various advantages over the threaded approach. It doesn't rely on daemon threads, so you can actually clean up the network connection (eg, to send a close message if necessary) rather than relying on the platform to destroy it. It handles all the actual low level networking code for you (your original example is doing the wrong thing in the case of socket.send returning 0; this code will handle that case properly). You also don't have to rely on ctypes or the obscure CPython API for raising an exception in another thread (so it's portable to more versions of Python and can actually interrupt a blocked send immediately, unlike some of the other suggested approaches).
Ensure that the "quit" is working correctly and add a small print to test that the input is working.
if var == "quit":
print "Hey we got quit"
The variable elapsed is not initialized. Set it to zero above the while loop.
It's easy to test the scope of killed:
>>> import thread
>>> killed = False
>>> import time
>>> def test():
... while True:
... time.sleep(1)
... if killed:
... print 'Dead.'
... break
...
>>> thread.start_new_thread(test, ())
25479680
>>> time.sleep(3)
>>> killed = True
>>> Dead.