Python Forum
Having difficulty with threads and input()
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Having difficulty with threads and input()
#1
Hello,

I have the below function running on it's own thread:

def handle_input(ws, stop_event):
    while not stop_event.is_set():
        if input("Press 'x' key then enter to exit...\n") == 'x': break
         
    #Close out any open orders
    open_indices = orders_df[orders_df['Status'] == 'Open'].index.tolist()
    if open_indices:
        for ordernum in open_indices: cancel_order(ordernum)
    if enter_positions and position: placeorder(symbol, shares_str, 'market', 'exit')
    global keep_running, shutdown_flag
    print('$Rolling Profit = ', round(rolling_profit + (profit * shares), 3))
    keep_running = False
    shutdown_flag = True
    ws.close()
The thread is called with:

# Thread for handling user input
stop_event = threading.Event()
input_thread = threading.Thread(target=handle_input, args=(ws, stop_event))
input_thread.start()
and continually runs from when I start the script.

The purpose of this thread is so that at any point I can type 'x' then enter and it gracefully terminates the script. Sometimes, however, I will want the script to stop automatically without my user input, that's why I have the stop_event flag. Specifically, I call:

stop_event.set()
From within another function when I want the script to automatically and gracefully exit.

The problem, to my best guess, is that the input() function, while waiting for input, precludes the while loop from cycling and thereby checking stop_event. I've tried modifying function handle_input with an added parameter so that I can independently call the function without using the while loop to do cleanup actions, but that still doesn't stop the open thread. I've gotten it working where it automatically stops, but when that worked the manual stop no longer worked... When calling the function independently I tried also using an exit() to just kill the script but that resorted in various infinite loops for me...

I'm kind of out of ideas short of using ctypes to hard kill the thread... which I'd rather avoid but I can try barring other ideas. However, if anyone has any ideas how I can overcome this I'd appreciate it, thanks.
Reply
#2
There is a section on shutting down threads in this blog.
Perhaps this will help with your problem.
Reply
#3
is that possible? When you write it. if input() is part of the declaration in a while loop. Sorry. Very nice script. But can you just use the back arrow key on the browser to exit the thread?
Programs are like instructions or rules. Learning it gets us closer to a solution. Desired outcome. Computer talk.
Reply
#4
You cannot use any blocking functions like input(). Instead of input() you could use termios for linux/mac or msvcrt for windows. I would use pygame since it works for everything.
Reply
#5
I like the idea of using pygame since I'm doing my dev on a local windows machine but eventually this script will be deployed to a linux server accessible only via SSH.

https://www.geeksforgeeks.org/how-to-get...in-pygame/

So the best method with pygame would be to use pygame to detect keyboard inputs, and if it detects "x" then "enter" it terminates the script, correct? That method would work on both a remote linux server which I SSH'd into, as well as my local machine?

On my local windows machine, I'm running the python script in powershell. Am I correct I'm assuming that pygame will only detect the input when the powershell is in the foreground? I don't want to be in another application doing stuff and then pygame by happenstance detects the input and then shuts down the script...
Reply
#6
Doesn't sound like pygame is a good fit.
Reply
#7
Disclaimer: does not work for what you want to do.
You can perhaps try this code where I interrupt the main thread which reads stdin by closing stdin in another thread. It works for me in Linux
import sys
import threading
from time import sleep

def work():
    sleep(3)
    sys.stdin.close()
    print("End of worker's task!")

def main():
    worker = threading.Thread(target=work)
    worker.start()
    try:
        for line in sys.stdin:
            print(line, end='')
    except ValueError as err:
        print(repr(err))
    finally:
        worker.join()

if __name__ == '__main__':
    main()
Output:
λ python paillasse/pf/inputthread.py spam spam eggs eggs End of worker's task! ValueError('I/O operation on closed file.')
EDIT: Unfortunately it does not work! the error is not thrown unless stdin tries to read a line after it was closed! You could as well check in the loop if an event is set.
« We can solve any problem by introducing an extra level of indirection »
Reply
#8
In Linux I know actual solutions with selectors, but they won't work in Windows because I don't think selectors work on stdin in Windows.

If your script was a server, you could interrupt it by connecting to it. This is multiplatform.
« We can solve any problem by introducing an extra level of indirection »
Reply
#9
How about using ctrl+c? If you are running from a console, ctrl+c raises a KeyboardInterrupt that your program can catch using try/except. Works for windows, osx, linux.
from time import sleep

print("Nighty night")
try:
    sleep(10)  # Standin for whatever work your script does
except KeyboardInterrupt:
    pass
print("Time to wake up")  # Standin for the cleanup.
Reply
#10
ctrl+c isn't practical - so this is a script that sends orders to a brokerage for stock execution - it connects and streams multiple different API services to receive real time market data, position data, submit orders and get order status.

I really need a graceful exit because upon exit I need to check if there are any unfilled, or partially filled orders, cancel them, and close out of all open stock positions. Then exit the script. That's what my function handle_input does.

I also really want a way to manually terminate the script in case I want to stop it early. I could use ctrl+c, but then I need to use the broker's desktop software to go into the trading account and manually clean up all open orders and close out open positions.

#This function is used to hard kill a thread
def hard_kill(tid):
    if not isinstance(tid, int):
        tid = tid.ident
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(SystemExit))
    if res == 0:
        raise ValueError("Invalid thread id")
    elif res != 1:
        # If it returns a number greater than one, it's a problem.
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
        raise SystemError("PyThreadState_SetAsyncExc failed")
I tried using the above to hard kill the thread, and then perform cleanup actions again, but it doesn't seem to terminate the script fully... I guess as long as it performs clean up actions and partially terminates the script (to the point where it won't send new orders) I don't care if it just keeps running.... The point is I can't have it send new orders or not close out existing positions because that could cost me a lot of real $$$... I can't test the above function robustly until tomorrow as even on a sim trading account I can't really simulate things accurately outside of market hours and the market is closed right now.

I need an auto shutdown in two cases - at 4 PM eastern, when the market closes, and when an order comes back with a "rejected" status... which theoretically should never happen but if it does and the script keeps running the subsequent behavior can become super weird, which could cost me real $$$ so I need to close out positions and stop trading until I manually figure out what happened and restart it.

Quote:If your script was a server, you could interrupt it by connecting to it. This is multiplatform.

This I am fascinated by... how can I connect to my script? If I can send an external signal to it to start termination activities that could work instead of using an input()... or another idea I had is to just have an empty text file, have the script check the file every second and if I save 'x' to the text file that initiates termination activities instead of input()... thoughts?
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Difficulty in adapting duplicates-filter in script ledgreve 5 1,061 Jul-17-2023, 03:46 PM
Last Post: ledgreve
  Difficulty with installation standenman 2 1,155 May-03-2023, 06:39 PM
Last Post: snippsat
  Difficulty with installation standenman 0 783 May-02-2023, 08:33 PM
Last Post: standenman
  Difficulty in understanding transpose with a tuple of axis numbers in 3-D new_to_python 0 1,617 Feb-11-2020, 06:03 AM
Last Post: new_to_python
  Difficulty installing Pycrypto KipCarter 4 13,212 Feb-10-2020, 07:54 PM
Last Post: snippsat

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020