Python LIRC blocking Signal workaround not working - python

I've been having trouble with the Python LIRC function lirc.nextcode(). I turned off blocking, which allows the code lirc.nextcode() to be skipped if the LIRC queue is empty, by initializing with lirc.init("program", blocking=False) and tried lirc.set_blocking(False, sockid). Neither worked and the code would always hang, waiting for a button press, when it should continue on.
I found this workaround that puts a time limit on raw_input('prompt'). So even if my lirc.nextcodde() waits for a button press, an alarm will go off after 5 seconds if no button has been pressed to deactivate the alarm, and skips the code anyway:
import signal
class AlarmException(Exception):
pass
def alarmHandler(signum, frame):
raise AlarmException
def nonBlockingRawInput(prompt='', timeout=20):
signal.signal(signal.SIGALRM, alarmHandler)
signal.alarm(timeout)
try:
text = raw_input(prompt)
signal.alarm(0)
return text
except AlarmException:
print '\nPrompt timeout. Continuing...'
signal.signal(signal.SIGALRM, signal.SIG_IGN)
return ''
Then changed it to fit my needs:
import signal
import lirc
sockid = lirc.init('weather', blocking=False)
class AlarmException(Exception):
pass
def alarmHandler(signum, frame):
raise AlarmException
def nonBlockingRawInput(prompt='', timeout=5):
signal.signal(signal.SIGALRM, alarmHandler)
signal.alarm(timeout)
try:
text = lirc.nextcode()
signal.alarm(0)
print text
return text
except AlarmException:
print '\nPrompt timeout. Continuing...'
signal.signal(signal.SIGALRM, signal.SIG_IGN)
print 'timed out'
return ''
nonBlockingRawInput()
What I want to happen: If a button has been pressed and an IR code is in the LIRC queue, it should print the button that was pressed. If no button has been pressed and the LIRC queue is empty, it should print "Prompt timeout. Continuing..." and "timed out".
What actually happens: If a button has been pressed and an IR code is in the LIRC queue it prints the button, but if no button has been pressed and the queue is empty it hangs until I close it.
It works exactly as intended until I change text = raw_input(prompt) to text = lirc.nextcode(), then it hangs on that function until it closes and gives this error:
Traceback (most recent call last):
File "/home/pi/time.py", line 27, in <module>
nonBlockingRawInput()
File "/home/pi/time.py", line 16, in nonBlockingRawInput
text = lirc.nextcode()
File "/home/pi/time.py", line 10, in alarmHandler
raise AlarmException
__main__.AlarmException
So not only does turning off blocking for lirc.nextcode() not work, but it also prevents the Signal alarm code workaround from continuing as well.
Here is a link "Python Lirc blocks code even when blocking is off" to my original question regarding LIRC blocking, which is what this workaround was for. I'll gladly accept an answer for either.
Thanks ahead of time for any help, it's really appreciated.

Switching to Pylirc2 and using pylirc.blocking(0) fixed it.

Related

signal.pause() with signal.alarm() causes RecursionError in non-sleeping program

Single-threaded python program, intending to be responsive to events from raspberry pi button presses also wants to wake every minute to update an LCD display.
Main function:
btn_1 = 21
GPIO.setup(btn_1, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.add_event_detect(btn_1, GPIO.FALLING, callback=btn_1_press_callback, bouncetime=100)
lcd.display()
lcd.messsage("text to display on lcd"
The previous code runs the btn_1_press_callback function whenever a physical button is pressed. The rest of the main function, instead of sleeping in a busy loop, does this:
signal.signal(signal.SIGALRM, wake_every_min)
signal.alarm(60)
signal.pause()
This way button presses are signaled immediately. The wake_every_minute() function simply refreshes the display with the currently displayed data (updating from the data source), so it updates every minute regardless of a button press:
def wake_every_min(sig, frame):
lcd.clear()
lcd.message("new string here")
signal.alarm(60)
signal.pause()
And then it calls signal.pause() to sleep / but listen for signals again. This works perfectly, except for the fact that after some time, I get RecursionError: maximum recursion depth exceeded while calling a Python object
Funny enough it's always at the same time, meaning "previous line repeated 482 times" is alway 482:
Traceback (most recent call last):
File "./info.py", line 129, in <module>
main()
File "./info.py", line 126, in main
signal.pause()
File "./info.py", line 111, in wake_every_min
signal.pause()
File "./info.py", line 111, in wake_every_min
signal.pause()
File "./info.py", line 111, in wake_every_min
signal.pause()
[Previous line repeated 482 more times]
Is there another way to accomplish this without a while True loop with a time.sleep()? If I do that, button presses aren't responsive, as there is always a potential for a 1.9999 minute delay, worst-case.
Update: I was thinking about this wrong. time.sleep() will not prevent a signal from happening -- the signals will interrupt sleep().
The correct solution is to sleep in the main loop, and never call signal.pause(). With a SIGINT handler, you can also exit immediately when ^c is pressed:
signal.signal(signal.SIGALRM, wake_every_min)
signal.alarm(60)
signal.signal(signal.SIGINT, int_signal_handler)
while True:
# Sleep and wait for a signal.
sleep(10)
signal.alarm(60)
Moving the re-setting of the alarm into the main loop prevents RecursionError, as new signals aren't piling up from the handler call stack. If anyone is curious what this was for, it's a crypto LCD ticker: https://github.com/manos/crypto_lcd/blob/master/info.py

How to stop youtube-dl download inside a thread using Python 3

I have a function that downloads videos from specific URLs and I launch this function through a thread to avoid GUI freezing, but I want a function to stop or pause the download. How to do this?
Here is the code:
def download_videos(self):
ydl1 = youtube_dl.YoutubeDL(self.get_opts())
if self.get_Urls().__len__() > 0:
ydl1.download(self.get_Urls())
def downloadVideoThrd(self):
self.t1 = threading.Thread(target=self.download_videos())
self.t1.start()
You can use these two options. Use multiprocessing library instead threading or raise SystemExit exception in the subthread
Note: When i tested youtube-dl, it is resume downloading from where it is left. That's why when we start to download same url, youtube-dl will resume, but downloaded file need to be in the filesystem
Here is the first option, in this solution, we not use thread, we use subprocess because we can send any signal to the subprocess and handle this signal whatever we want.
import multiprocessing
import time
import ctypes,signal,os
import youtube_dl
class Stop_Download(Exception): # This is our own special Exception class
pass
def usr1_handler(signum,frame): # When signal comes, run this func
raise Stop_Download
def download_videos(resume_event):
signal.signal(signal.SIGUSR1,usr1_handler)
def download(link):
ydl1 = youtube_dl.YoutubeDL()
ydl1.download([link])
try:
download(link)
except Stop_Download: #catch this special exception and do what ever you want
print("Stop_Download exception")
resume_event.wait() # wait for event to resume or kill this process by sys.exit()
print("resuming")
download(link)
def downloadVideoThrd():
resume_event=multiprocessing.Event()
p1 = multiprocessing.Process(target=download_videos,args=(resume_event,))
p1.start()
print("mp start")
time.sleep(5)
os.kill(p1.pid,signal.SIGUSR1) # pause the download
time.sleep(5)
down_event.set() #resume downlaod
time.sleep(5)
os.kill(p1.pid,signal.SIGUSR2) # stop the download
downloadVideoThrd()
Here is the second solution, you can also check this for more detail about killing the thread. We will raise a SystemExit exception in the subthread by main thread. We can both stop or pause thread. To pause thread you can use threading.Event() class. To stop the thread(not process) you can use sys.exit()
import ctypes,time, threading
import youtube_dl,
def download_videos(self):
try:
ydl1 = youtube_dl.YoutubeDL(self.get_opts())
if self.get_Urls().__len__() > 0:
ydl1.download(self.get_Urls())
except SystemExit:
print("stopped")
#do whatever here
def downloadVideoThrd(self):
self.t1 = threading.Thread(target=self.download_videos)
self.t1.start()
time.sleep(4)
"""
raise SystemExit exception in self.t1 thread
"""
ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(self.t1.ident),
ctypes.py_object(SystemExit))

How to avoid python exception in while handling SIGINT

import signal
import time
def sigint_handler(signum, frame):
print 'User pressed CTRL+C!'
signal.signal(signal.SIGINT, sigint_handler)
def main():
while True:
print 'Script to handle SIGINT'
time.sleep(2)
##########
if __name__ == "__main__":
main()
How can I block this below exception thrown by python itself when executing the code:
File "D:\Documents\scripts\ctrlc handler.py", line 19, in <module> main()
File "D:\Documents\scripts\ctrlc handler.py", line 14, in main
time.sleep(2) OError: [Errno 4] Interrupted function call
The issue seems to have something to do with different signals being received and different errors being thrown. See also this maybe for reference. The work around I could come up with was to catch and ignore the OSError/IOError that is being thrown like so:
import signal
import time
def sigint_handler(signum, frame):
print 'User pressed CTRL+C!'
signal.signal(signal.SIGINT, sigint_handler)
def main():
while True:
print 'Script to handle SIGINT'
try:
time.sleep(2)
except IOError:
pass
##########
if __name__ == "__main__":
main()
Which works perfectly.
Script to handle SIGINT
User pressed CTRL+C!
Script to handle SIGINT
User pressed CTRL+C!
Script to handle SIGINT
User pressed CTRL+C!
Script to handle SIGINT
User pressed CTRL+C!
Script to handle SIGINT
User pressed CTRL+C!
Script to handle SIGINT
User pressed CTRL+C!
Script to handle SIGINT
User pressed CTRL+C!
Script to handle SIGINT
User pressed CTRL+C!

How can I end an infinite loop with socket operations inside after finishing current iteration?

I have an infinite loop in which there are operations that are mandatory to be completely executed before exiting the loop. Namely, I am using the socket library for connecting to an external device and I need to wait the read instructions to be finished before interrupting the loop.
I have tried using a signal handler (like in this question) for raising a flag when a Keyboard interrupt is detected.
Current code:
import videosensor
import signal
def signal_handler(signal, frame):
"""Raises a flag when a keyboard interrupt is raised."""
global interrupted
interrupted = True
if __name__ == '__main__':
camera = videosensor.VideoSensor(filename)
interrupted = False
signal.signal(signal.SIGINT, signal_handler)
while not interrupted:
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
#This code has to be executed after exiting while loop
camera_shutdown(camera)
In the previous code, videosensor.VideoSensor is a class containing socket operations for getting data from an external device. The get_register() method used in the main routine is the following:
def get_register(self):
"""Read the content of the specified register.
"""
#Do some stuff
value = socket.recv(2048)
return value
The problem:
I wanted the while loop to be continually executed until the user pressed a key or used the Keyboard Interrupt, but after the current iteration was finished. Instead, using the previous solution does not work as desired, as it interrupts the ongoing instruction, and if it is reading the socket, an error is raised:
/home/.../client.pyc
in read_register(self, regkey)
164 reg = self._REGISTERS[regkey]
165 self.send('r,{}\n'.format(reg))
--> 166 value = socket.recv(2048)
167 #Convert the string input into a valid value e.g. list or int
168 formatted_result = ast.literal_eval(value)
error: [Errno 4] Interrupted system
EDIT: It seems, from an answer below, that there is no way of using the Keyboard Interrupt and avoid the socket read function to be aborted. Despite there are solutions for catching the error, they don't avoid the read cancellation.
I am interested, though, in finding a way of getting a user input e.g. specific key press, that raises the flag, which will be checked at the end of the loop, without interrupting the main routine execution until this check.
EDIT2: The used OS is the Linux distribution Ubuntu 14.04
After quick SO search I found this solution for your issue
Basically, there's nothing you can do: when you send a SIGINT to your process, the socket will return a SIGINT as well. The best you can do, then, is to actively ignore the issue, by catching the socket EINTR error and going on with your loop:
import errno
try:
# do something
value = conn.recv(2048)
except socket.error as (code, msg):
if code != errno.EINTR:
raise
An alternative solution to avoid issues with C-c breaking reads, is to use parallel execution, to read your socket in a routine, and handle user input on the other:
import asyncio
async def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
await asyncio.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
async def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = asyncio.Event()
asyncio.Task(camera_task(end_event, filename))
asyncio.Task(input_task(end_event))
asyncio.get_event_loop().run_forever()
or with threading
import threading, time
def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = threading.Event()
threads = [
threading.Thread(target=camera_task, args=(end_event, filename)),
threading.Thread(target=input_task, args=(end_event,))
]
# start threads
for thread in threads:
thread.start()
# wait for them to end
for thread in threads:
thread.join()
or with multiprocessing:
import multiprocessing, time
def camera_task(has_ended, filename):
camera = videosensor.VideoSensor(filename)
try:
while not has_ended.is_set():
location = camera.get_register()
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
finally:
#This code has to be executed after exiting while loop
camera_shutdown(camera)
def input_task(shall_end):
while True:
i = input("Press 'q' to stop the script…")
if i == 'q':
shall_end.set()
def main():
filename = …
#
end_event = multiprocessing.Event()
processes = [
multiprocessing.Process(target=camera_task, args=(end_event, filename)),
multiprocessing.Process(target=input_task, args=(end_event,))
]
# start processes
for process in processes:
process.start()
# wait for them to end
for process in processes:
process.join()
disclaimer: those codes are untested, and there might be some typos or little errors, but I believe the overall logic should be 👌
You created your custom signal handler but did not overide the default keyboard interrupt behaviour. Add signal.signal(signal.SIGINT, signal_handler) to your code to accomplish this:
import videosensor
import signal
# Custom signal handler
def signal_handler(signal, frame):
"""Raises a flag when a keyboard interrupt is raised."""
global interrupted
interrupted = True
# Necessary to override default keyboard interrupt
signal.signal(signal.SIGINT, signal_handler)
if __name__ == '__main__':
# Main programme
If I understand correctly, you do not want socket.recv() to be interrupted, but you do want to use signals to let the user indicate that the I/O loop should be terminated once the current I/O operation has completed.
With the assumption that you are using Python 2 on a Unix system, you can solve your problem by calling signal.siginterrupt(signal.SIGINT, False) before entering the loop. This will cause system calls to be restarted when a signal occurs rather than interrupting it and raising an exception.
In your case this means that the socket.recv() operation will be restarted after your signal handler is called and therefore get_register() will not return until a message is received on the socket. If that is what you want your code will be:
interrupted = False
old_handler = signal.signal(signal.SIGINT, signal_handler) # install signal handler
signal.siginterrupt(signal.SIGINT, False) # do not interrupt system calls
while not interrupted:
location = camera.get_register()
if location == '':
# remote connection closed
break
#...
#More irrelevant stuff is executed.
#...
time.sleep(0.01)
That's one way to do it, but it does require that your code is running on a Unix platform.
Another way, which might work on other platforms, is to handle the exception, ignore further SIGINT signals (in case the user hits interrupt again), and then perform a final socket.recv() before returning from the get_register() function:
import errno
def get_register(s):
"""Read the content of the specified register.
"""
#Do some stuff
try:
old_handler = None
return s.recv(2048)
except socket.error as exc:
if exc.errno == errno.EINTR:
old_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) # ignore this signal
return s.recv(2048) # system call was interrupted, restart it
else:
raise
finally:
if old_handler is not None:
signal.signal(signal.SIGINT, old_handler) # restore handler
Signal handling can get tricky and there might be race conditions in the above that I am not aware of. Try to use siginterrupt() if possible.

How can control be returned from a signal handler function that has been defined within a function?

I'm writing a small, single function that is designed to request user input with a time delay. When the time delay runs out, the function should return None instead of a user's response and then should continue with the rest of the script.
In the current implementation, the user input works and the timeout works, with the timeout message printed by a signal handler function which is defined within the function (I'm aiming to have this outer function fairly self-contained). However, processing then halts (rather than exiting the while loop defined in the main function) and I'm not sure why.
How can I get processing to continue? Am I misusing signal in some way? Could a lambda be used in place of an explicitly-defined function for the handler function?
#!/usr/bin/env python
from __future__ import print_function
import signal
import propyte
def main():
response = "yes"
while response is not None:
response = get_input_nonblocking(
prompt = "ohai? ",
timeout = 5
)
print("start non-response procedures")
# do things
def get_input_nonblocking(
prompt = "",
timeout = 5,
message_timeout = "prompt timeout"
):
def timeout_manager(signum, frame):
print(message_timeout)
#signal.signal(signal.SIGALRM, lambda: print(message_timeout))
signal.signal(signal.SIGALRM, timeout_manager)
signal.alarm(timeout)
try:
response = propyte.get_input(prompt)
return response
except:
return None
if __name__ == '__main__':
main()
What you've got is almost there, but you need to raise an exception inside your signal handler. raw_input will block until something happens, either input or an exception. If you raise an exception in the signal handler, that will then interrupt raw_input and execution will fall into the except in your get_input_non_blocking function. Here's a toy example.
import signal
def timeout(signum, frame):
raise IOError("bye!")
signal.signal(signal.SIGALRM, timeout)
def input():
try:
print("omgplz: ")
return raw_input()
except IOError:
return None
signal.alarm(5)
txt = input()
signal.alarm(0)
print(txt)
There's some more discussion and an alternative approach using select in this answer here: Keyboard input with timeout in Python
Hope that helps!

Categories