I change a global variable in a signal handler and poll for it in the main program. But the value does not change in the main thread.
Is there a qualifier that I need to use to make it a volatile (like in Java) variable?
Here's the program:
test.py
import time
import signal
def debug():
closeSession = False
def sigint_handler(signal, frame):
global closeSession
print('Breaking the poll...')
closeSession=True
signal.signal(signal.SIGINT, sigint_handler)
# Start a program...
while not closeSession:
time.sleep(1)
print('Polling... closeSession = %r' % closeSession)
print('Exiting! Bye.')
# Sent 'quit' to stdin of the program
if __name__ == "__main__":
debug()
sigint_handler() gets called whenever I press Ctrl + C but the new value of closeSession is not used in the main thread.
I get the following output:
$ python test.py Polling... closeSession = False Polling...
closeSession = False
I press Ctrl + C
^CBreaking the poll... Polling... closeSession = False
Press Ctrl + C, again
^CBreaking the poll... Polling... closeSession = False
Press Ctrl + C, again
^CBreaking the poll... Polling... closeSession = False
Polling... closeSession = False
The problem is scope.
Inside the debug() function, you didn't declare closeSession as a global, which means that you have two variables called closeSession. One global and one scoped within the debug() function. And inside the sigint_handler() function, you've explicitly instructed to use global one, which is shadowed by the scoped one in the outer function.
You can solve this by declaring global before assignment in debug():
def debug():
global closeSession
closeSession = False
...
By the way, your code does not work on windows, it throws a IOError because the sleep function is interrupted. A workaround that worked for me is:
...
while not closeSession:
try:
time.sleep(1)
except IOError:
pass
print('Polling... closeSession = %r' % closeSession)
...
It's not pretty but it works.
You have to set global closeSession before accessing the variable, else you're creating a local variable with the same name and the loop will never end.
Try this:
import time
import signal
def debug():
global closeSession # <-- this was missing
closeSession = False
def sigint_handler(signal, frame):
global closeSession
print('Breaking the poll...')
closeSession=True
signal.signal(signal.SIGINT, sigint_handler)
# Start a program...
while not closeSession:
time.sleep(1)
print('Polling... closeSession = %r' % closeSession)
print('Exiting! Bye.')
# Sent 'quit' to stdin of the program
if __name__ == "__main__":
debug()
Related
I have three files:
helper.py
from globalvariables import *
global exit_signal
global exited_signal
def helperstart():
global exited_signal
while True:
if exit_signal is True:
exited_signal = True
print ('Exiting from helper thread')
return
main.py
from globalvariables import *
import threading
import helper
global exit_signal
global exited_signal
def mainstart():
global exit_signal
helper_thread = threading.Thread(target = helper.helperstart)
input ('Press <enter> to start the thread')
helper_thread.start()
input ('Press <enter> to end the thread')
exit_signal = True
# check if helper has exited
if exited_signal is True:
print ('Helper exited successfully')
if __name__ == '__main__':
mainstart()
globalvariables.py
exit_signal = False
exited_signal = False
From main.py, the value of exit_signal should be edited to True. This should make the helper thread exit. But it is not exiting. I've tried printing the value of exit_signal from helperstart() function and it keeps showing as False. So, the main.py isn't editing the variable properly. Please help me figure out why.
The thread started in the start_thread method does not stop. Why ?
import time
import threading
cont_running = True
def start_thread():
threading.Thread(target=run).start()
def stop_thread():
cont_running = False
def run():
while cont_running:
print 'Thread running : ' + str(cont_running)
time.sleep(0.2)
print 'Thread ended'
start_thread()
time.sleep(2)
stop_thread()
In stop_thread(), your assignment statement creates a local variable named cont_running. This local variable is unrelated to the global variable of the same name.
Try this:
def stop_thread():
global cont_running
cont_running = False
I created a thread for a keylogger that logs in parallel to another thread that produces some sounds ( I want to catch reaction times).
Unfortunately, the thread never finishes although i invoke killKey() and "invoked killkey()" is printed.
I allways get an thread.isActive() = true from this thread.
class KeyHandler(threading.Thread):
hm = pyHook.HookManager()
def __init__(self):
threading.Thread.__init__(self)
def OnKeyboardCharEvent(self,event):
print 'Key:', event.Key
if event.Key=='E':
...
return True
def killKey(self):
KeyHandler.hm.UnhookKeyboard()
ctypes.windll.user32.PostQuitMessage(0)
print "invoked killkey()"
def run(self):
print "keyHandlerstartetrunning"
KeyHandler.hm.KeyDown = self.OnKeyboardCharEvent
KeyHandler.hm.HookKeyboard()
#print "keyboardhooked"
pythoncom.PumpMessages()
to be more precise,
ctypes.windll.user32.PostQuitMessage(0) does nothing
I would favor an external timeout to invoke killKey(), respective ctypes.windll.user32.PostQuitMessage(0) in this thread.
PostQuitMessage has to be posted from the same thread. To do so you need to introduce a global variable STOP_KEY_HANDLER. If you want to quit then just set global STOP_KEY_HANDLER = True from any thread you want and it will quit with the next keystroke. Your key handler has to run on the main thread.
STOP_KEY_HANDLER = False
def main():
pass # here do all you want
#bla bla
global STOP_KEY_HANDLER
STOP_KEY_HANDLER = True # This will kill KeyHandler
class KeyHandler:
hm = pyHook.HookManager()
def OnKeyboardCharEvent(self,event):
if STOP_KEY_HANDLER:
self.killKey()
print 'Key:', event.Key
if event.Key=='E':
pass
return True
def killKey(self):
global STOP_KEY_HANDLER
if not STOP_KEY_HANDLER:
STOP_KEY_HANDLER = True
return None
KeyHandler.hm.UnhookKeyboard()
ctypes.windll.user32.PostQuitMessage(0)
print "invoked killkey()"
def _timeout(self):
if self.timeout:
time.sleep(self.timeout)
self.killKey()
def run(self, timeout=False):
print "keyHandlerstartetrunning"
self.timeout = timeout
threading.Thread(target=self._timeout).start()
KeyHandler.hm.KeyDown = self.OnKeyboardCharEvent
KeyHandler.hm.HookKeyboard()
#print "keyboardhooked"
pythoncom.PumpMessages()
k=KeyHandler()
threading.Thread(target=main).start()
k.run(timeout=100) # You can specify the timeout in seconds or you can kill it directly by setting STOP_KEY_HANDLER to True.
I guess pbackup's solution is fine. Just to conclude I found a solution by simply sending a key myself instead of waiting for the user to input. It's proably not the best but was the fastest an goes parallel in my timing thread with the other timing routines.
STOP_KEY_HANDLER = True
# send key to kill handler - not pretty but works
for hwnd in get_hwnds_for_pid (GUIWINDOW_to_send_key_to.pid):
win32gui.PostMessage (hwnd, win32con.WM_KEYDOWN, win32con.VK_F5, 0)
# sleep to make sure processing is done
time.sleep(0.1)
# kill window
finished()
So I have this code:
import time
import threading
bar = False
def foo():
while True:
if bar == True:
print "Success!"
else:
print "Not yet!"
time.sleep(1)
def example():
while True:
time.sleep(5)
bar = True
t1 = threading.Thread(target=foo)
t1.start()
t2 = threading.Thread(target=example)
t2.start()
I'm trying to understand why I can't get bar to = to true.. If so, then the other thread should see the change and write Success!
bar is a global variable. You should put global bar inside example():
def example():
global bar
while True:
time.sleep(5)
bar = True
When reading a variable, it is first searched inside the function and if not found, outside. That's why it's not necessary to put global bar inside foo().
When a variable is assigned a value, it is done locally inside the function unless the global statement has been used. That's why it's necessary to put global bar inside example()
You must specify 'bar' as global variable. Otherwise 'bar' is only considered as a local variable.
def example():
global bar
while True:
time.sleep(5)
bar = True
I have a signal handler to handle ctrl-c interrupt. If in the signal handler I want to read a variable set in my main script, is there an alternative to using a "global" statement when setting the variable?
I don't mind doing this, but read this post (Do you use the "global" statement in Python?) in which someone commented that there should be no reason to ever use global.
What is the alternative in this case?
My code looks like this:
def signal_handler(signal, frame):
print "in sig handler - g_var=%s" % g_var
def main():
global g_var
g_var = "test"
time.sleep(120)
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
main()
You can use a closure as the signal handler that acquires its state from the main script:
import signal
import sys
import time
def main_function():
data_for_signal_handler = 10
def signal_handler(*args):
print data_for_signal_handler
sys.exit()
signal.signal(signal.SIGINT, signal_handler) # Or whatever signal
while True:
data_for_signal_handler += 1
time.sleep(0.5)
if __name__ == '__main__':
main_function()
You can use partial to create a "closure".
import signal
from functools import partial
def signal_handler(g_var, signal, frame):
print "in sig handler - g_var=%s" % g_var
def main():
g_var = "test"
signal.signal(signal.SIGINT, partial(signal_handler, g_var))
time.sleep(120)
if __name__ == '__main__':
main()
Within the object-oriented paradigm (OOP) it's quite convenient to use lambdas for that purpose. Using lambdas you could pass some additional context (like a self reference) and/or get rid of the unused arguments (signal, frame).
import time
import signal
class Application:
def __init__( self ):
signal.signal( signal.SIGINT, lambda signal, frame: self._signal_handler() )
self.terminated = False
def _signal_handler( self ):
self.terminated = True
def MainLoop( self ):
while not self.terminated:
print( "I'm just doing my job like everyone else" )
time.sleep( 3 )
app = Application()
app.MainLoop()
print( "The app is terminated, exiting ..." )
If you're just reading the variable, there should be no need to make the variable "global"
def foo():
print a
a = 3
foo() #3
global is necessary to allow you to change the variable and have that change propagate into the module namespace.
If you want to pass some state to your callback without using global, the typical way to do this us to use an instance method as the callback:
class foo(object):
def __init__(self,arg):
self.arg = arg
def callback_print_arg(self):
print self.arg
def call_callback(callback):
callback()
a = foo(42)
call_callback(a.callback_print_arg) #42
You can access outer-scope variables from within an inline-defined function, like so:
my_values = {'foo':'bar'}
def handler(signum, frame):
for key,val in my_values.items():
print key,val
my_values['bat']='baz'
#remember to use mutable types, like dicts or lists
signal.signal(signal.SIGINT, handler)