python-prompt-toolkit spawns a lot of threads - python

I'm really only using it for command history up/down functionality on windows - I couldn't get pyreadline3 to work correctly. Everything works fine, but using the up and down keys creates up to 40 threads that never go away.
from prompt_toolkit import prompt
from prompt_toolkit.history import FileHistory
while True:
print("\n")
print("=============")
print("Enter command")
command_text = prompt('>', history=FileHistory('command_history.txt'),)
cmd = command_text.lower().strip()
if cmd == "": # skip empty input
continue
if cmd == "exit": # bail
break
process_command(cmd)
That's basically it. Is this normal behavior? I guess the threadpool just creates new threads up to its maximum, but it seems a little excessive for this task, and makes my VScode call stack a mess.

Related

Methods to restart died process

There's a little app named logivew that I'm writing a script to monitor, along with some other tasks. In the main while loop (which will exit when the app I'm most concerned about closes), I check to see if logview needs restarting. The code I have presently is roughly as follows:
#a good old global
logview = "/usr/bin/logview"
#a function that starts logview:
port = 100
log_file = "/foo/bar"
logview_process = subprocess.Popen([logview, log_file, port],
stdout = subprocess.DEVNULL,
stderr = subprocess.STDOUT)
#a separate function that monitors in the background:
while True:
time.sleep(1)
logview_status = 0
try:
logview_status = psutil.Process(logview_process.pid).status()
except psutil.NoSuchProcess:
pass
if(logview_status == psutil.STATUS_STOPPED or
logview_status == psutil.STATUS_ZOMBIE or
logview_status == psutil.STATUS_DEAD or
logview_status == 0):
print("Logview died; restarting")
logview_cli_list = [logview]
logview_cli_list.extend(logview_process.args)
logview_process = subprocess.Popen(logview_cli_list,
stdout = subprocess.DEVNULL,
stderr = subprocess.STDOUT)
if(some_other_condition): break
However, if I test-kill logview, the condition triggers and I do see the printed message, but then I see it again, and again, and again. It seems that the condition triggers every single iteration of the loop if logview does die. And, it never does get restarted properly.
So clearly... I'm doing something wrong. =)
Any help (or better methods!) would be greatly appreciated.
I don't know your logview program but the problem is here:
logview_cli_list = [logview]
logview_cli_list.extend(logview_process.args)
When you're creating the argument list, you're putting logview twice in your command, because logview_process.args also contains the name of the launched command, so the program probably fails immediately because of bad args, and is run again and again...
The fix is then obvious:
logview_cli_list = logview_process.args
a better fix would be to create the process in the loop if a given flag is set and set the flag at the start.
When process dies, set the flag to trigger the process creation again. Would have avoided this copy/almost paste mistake.

Print line by line when running a process in python?

I recently worked on using multithreading but ran into an issue where it seems that multiprocessing would be the better way to go. When I run a simple loop counter function as a process, why doesn't it iterate through the loop and print out the output? Instead the code waits for a set amount of time before producing the output. Is there a way this can be solved or am I stuck dealing with processes this way?
import multiprocessing, time
def loop_process(process_name):
loopCnt = 0
print "\nstarting {}".format(process_name)
for loopCnt in range(15):
print("value of loopCnt = {}".format(loopCnt))
loopCnt += 1
time.sleep(1)
print('stopping {}'.format(process_name))
if __name__ == '__main__':
L00P_process = multiprocessing.Process(target=loop_process, args=('L00P_process',))
L00P_process.start()
L00P_process.join()
print('processes stopped')
print "Exiting Main"
I'm not clear on what you're seeing. On a Windows box just now, running the program from an interactive console ("DOS box"), I saw value of loopCnt = ... once per second until the program ended. That's what I expected.
Which OS are you running under, how are you running the program, and what exactly are you seeing?
On most (all?) machines, standard output (stdout) is line-buffered if it's attached to an interactive terminal. Which means output is forced to display each time a line boundary is hit. For other kinds of output device, it may use other kinds of buffering, and that could account for a delay.
Something to try: first add
import sys
near the top, then add
sys.stdout.flush()
after your
print("value of loopCnt = {}".format(loopCnt))
That should tell us whether you are, or are not, experiencing a problem with output buffering.

Stop opening VLC in python

I wrote a little code which scans the wifi signals in the air and according to the amount plays one or another audio file.
import time
import subprocess
import os
while True:
output = subprocess.Popen("airport en1 -s", shell=True, stdout=subprocess.PIPE).stdout.read()
lines = output.split('\n')
n = len(lines)
if n < 10:
print os.path.exists('/Users/shirin/Desktop/wifi/1.mp3')
p = subprocess.Popen(['/Applications/VLC.app/Contents/MacOS/VLC','/Users/shirin/Desktop/wifi/1.mp3'])
elif n > 10:
print os.path.exists('/Users/shirin/Desktop/wifi/1.mp3')
p = subprocess.Popen(['/Applications/VLC.app/Contents/MacOS/VLC','/Users/shirin/Desktop/wifi/1.mp3'])
Now my problem is that, when I run the file through the terminal it keeps running the code over and over again and I do not know how to stop it. It also does not stop when I close the terminal.
So my question:
How can I tell python to run the code once and then stop?
already super thanks!
Two things:
You have a while True: and no condition to break the loop, insert a break after reproducing the audio file so the loop could stop.
Even if you close the program VLC would be running until you manually stop it. This is because you are just calling an external command. If you want to reproduce the audio from python you can use another library like pygame
You can call p.terminate() if you want to terminate your application.
You can also call p.kill() if your application is not responsive.
Now, let's say you want to stop the application and stop VLC.
Have except KeyboardInterrupt: in your code and call p.terminate() when you stop the script.
while True:
try:
... #code goes here.
except KeyboardInterrupt:
p.terminate()

How do I abort reading a user's input in Python 3.x?

I have a thread that monitors user input which looks like this:
def keyboard_monitor(temp): #temp is sys.stdin
global flag_exit
while True:
keyin = temp.readline().rstrip().lower()
if keyin == "exit":
flag_exit = True
print("Stopping...")
if flag_exit == True:
break
If I type exit the flag is properly set and all the other threads terminate. If another one of my threads sets the flag, this thread refuses to finish because it's hanging on the user input. I have to input something to get it to finish. How do I change this code so that the program finishes when the flag is set externally?
Its hard to tell exactly what is going wrong without more of your code, but as an easy solution you could rather exit() which is a python built in. This should reliably terminate the application, also sys.exit()
From wim's comment:
You can use the atexit module to register clean up handlers
import atexit
def cleanup():
pass
# TODO cleanup
atexit.register(cleanup)

run a python program on a new thread

I have two programs
program1.py is like commandline interface which takes command from user
program2.py has the program which runs the relevant program as per the command.
Program 1 has also has an quit_program() module
In our simple universe.. lets say I have just one command and just one program
So lets say...
program1.py
def main():
while True:
try:
command = raw_input('> ')
if command == "quit" :
return
if command == '':
continue
except KeyboardInterrupt:
exit()
parseCommand(command)
And then I have:
if commmand == "hi":
say_hi()
Now program2 has
def say_hi():
#do something..
Now there can be two cases...
Either say_hi() completes in which case no issue...
But what i want is that if user enters a command (say: end)
then this say_hi() is terminated in between..
But my current implementation is very sequential.. I mean I dont get to type anything on my terminal untill the execution is completed..
Somethng tells me that the say_hi() should be running on another thread?
I am not able to think straight about this.
Any suggestions?
Thanks
The threading module is what you are looking for.
import threading
t = threading.Thread(target=target_function,name=name,args=(args))
t.daemon = True
t.start()
The .daemon option makes it so you don't have to explicitly kill threads when your app exits... Threads can be quite nasty otherwise
Specific to this question and the question in the comments, the say_hi function can be called in another thread as such:
import threading
if commmand == "hi":
t = threading.Thread(target=say_hi, name='Saying hi') #< Note that I did not actually call the function, but instead sent it as a parameter
t.daemon = True
t.start() #< This actually starts the thread execution in the background
As a side note, you must make sure you are using thread safe functions inside of threads. In the example of saying hi, you would want to use the logging module instead of print()
import logging
logging.info('I am saying hi in a thread-safe manner')
You can read more in the Python Docs.

Categories

Resources