Async socket.send() exception - python-3.x

Hello I have the following for my async loop
async def start_process(restore_items, args, loop):
with GlacierRestorer(args.temp_dir, args.error_log_bucket, loop) as restorer:
restorer.initiate_restore_all(restore_items)
tasks = []
semaphore = asyncio.BoundedSemaphore(4)
for item in restore_items:
tasks.append(asyncio.ensure_future(restorer.transfer(item, semaphore)))
await asyncio.gather(*tasks)
def main():
args = get_args()
restore_items = get_restore_items(args)
for item in restore_items:
print(item.source, ':', item.destination)
try:
loop = asyncio.get_event_loop()
loop.run_until_complete(start_process(restore_items, args, loop))
except KeyboardInterrupt:
pass
My job and files get larger I see that I keep getting an
socket.send() exception
After reading the documentation it seems to be coming from loop.run_until_complete
The exception doesn't come the program to crash, but eventually bogs it down so much it gets stuck printing the exception.
How do I modify the current code to fix this?

run_until_complete only propagates the exception raised inside start_process. This means that if an exception happens at any point during start_process, and start_process doesn't catch it, run_until_complete(start_process()) will re-raise the same exception.
In your case the exception likely originally gets raised somewhere in restorer.transfer(). The call to gather returns the results of the coroutines, which includes raising an exception, if one occurred.
The exception doesn't come the program to crash, but eventually bogs it down so much it gets stuck printing the exception. How do I modify the current code to fix this?
Ideally you would fix the cause of the exception - perhaps you are sending in too many requests at once, or you are using the GlacierRestorer API incorrectly. But some exceptions cannot be avoided, e.g. ones caused by a failing network. To ignore such exceptions, you can wrap the call to restorer.transfer in a separate coroutine:
async def safe_transfer(restorer, item, semaphore):
try:
return await restorer.transfer(item, semaphore)
except socket.error as e:
print(e) # here you can choose not to print exceptions you
# don't care about if doing so bogs down the program
In start_process you would call this coroutine instead of restorer_transfer:
coros = []
for item in restore_items:
coros.append(safe_transfer(restorer, item, semaphore))
await asyncio.gather(*coros)
Note that you don't need to call asyncio.ensure_future() to pass a coroutine to asyncio.gather; it will be called automatically.

Related

How do test the side effects of an except block with pytest?

My goal is to throw an exception during a test to trigger an except block, then wait for the except block to complete, and then assert stuff.
This is the method that needs to be tested
def main(args, symbol_store):
t = None
try:
t = parse_and_download(args.output, args.symbols, symbol_store)
t.wait()
except KeyboardInterrupt:
if t is not None:
t.set()
print(string_downloads_cancelled, flush=True)
The test method should do something like this
raise KeyboardInterrupt
assert not os.path.exists(test_symbol_store)
The assertion is clearly unreachable, and even if it was, I am certain that it would not wait for the except block to complete.
Following the advice of MrBean Breman I mocked the wait function with pytest_mocker.
mocker.patch('main.DownloadManager.DownloadThread.wait', side_effect=KeyboardInterrupt)
This works as intended.

Catching Outer Exceptions in Python

My code tries to do something, but it triggers an Error... which triggers another Error. So, the error message looks something like this:
SillyError: you can`t do that becuz blablabla
The above exception was the direct cause of the following exception:
LoopyError: you can`t do that becuz blobloblo
I want to create a try except block that only catches this specific duo of errors. However, I am only able to catch the first one, because once I do, the second one never gets a chance to trigger.
This other question is about catching either exception, but I want to catch only if both are triggered in succession. Is there a way?
If you have a try\except, you will always catch the error based on the outer exception. However you do have the option to pass on any exceptions you don't want to process.
In this code, the ZeroDivisionError is caught and wrapped in another exception which is then caught by the calling code. The calling code checks for the inner exception and decides whether to re-raise the exception up the stack.
def xtest():
try:
a = 1/0 # exception - division by zero
except ZeroDivisionError as e:
raise Exception("Outer Exception") from e # wrap exception (e not needed for wrap)
try:
xtest()
except Exception as ex:
print(ex) # Outer Exception
print(ex.__cause__) # division by zero
if (str(ex) == "Outer Exception" and str(ex.__cause__) == "division by zero"):
print("Got both exceptions")
else:
raise # pass exception up the stack
Just for completion, you can do the check based on the exception class name also:
if (type(ex).__name__ == "Exception" and type(ex.__cause__).__name__ == "ZeroDivisionError"):
print("Got both exceptions")
#ShadowRanger pointed out that it may be quicker to just check the class type instead of the class name:
if (type(ex) == Exception and type(ex.__cause__) == ZeroDivisionError):
print("Got both exceptions")

Can't reach except block in python

I want to loop through a certain line of code until any exception or keyboard interruption occurs. But I can not reach the exception block whenever any exception occurs or due to keyboard interruption.
How can I modify my code so that I could actually reach in case of exception being thrown?
def run():
lidar = RPLidar(PORT_NAME)
iterator = lidar.iter_scans(50000)
time.sleep(2)
environment(iterator)
while True:
try:
print('Hi')
update_line(iterator)
except Exception or KeyboardInterrupt:
print("exception occur. Run again")
#lidar = RPLidar(PORT_NAME)
lidar.stop_motor()
lidar.stop()
lidar.disconnect()
break
if __name__ == '__main__':
run()
I'm surprised that code actually runs. When you say except Exception or KeyboardInterrupt you are saying only take the first thing here that evaluates to True. Since bool(Exception) is True you are only going to catch Exceptions. To catch multiple types of exceptions you would write it like this:
try:
except (Exception, KeyboardInterrupt):
It might not be triggering or non-keyboard exceptions because the exception you are trying to catch derives from BaseException and not Exception. To fix that change Exception to BaseException.
def run():
while True:
try:
print('Hi')
function_doesnt_exist(iterator)
except Exception or KeyboardInterrupt:
print("exception occur. Run again")
break
if __name__ == '__main__':
run()
When I intentionally call function that doesn't exist in your while loop it calls exception : exception occur. Run again
Perhaps you didn't generate an error properly therefore no exception is called
Also , Exception or KeyboardInterrupt means Exception since Exception includes KeyboardInterrupt and u handle them in same manner
so If you just want to catch Keyboard Interrupt then go for:
except KeyboardInterrupt:
pass
Or if you want to handle general exception and Keyboard one different do something like this:
except KeyboardInterrupt:
print("don't press ctrl+C")
pass
except:
print("exception occured")
pass

Asyncio shared object at the same address does not hold same values

Okay, so I am created a DataStream object which is just a wrapper class around asyncio.Queue. I am passing this around all over and everything is working fine up until the following functions. I am calling ensure_future to run 2 infinite loops, one that replicates the data in one DataStream object, and one that sends data to a websocket. here is that code:
def start(self):
# make sure that we set the event loop before we run our async requests
print("Starting WebsocketProducer on ", self.host, self.port)
RUNTIME_LOGGER.info(
"Starting WebsocketProducer on %s:%i", self.host, self.port)
#Get the event loop and add a task to it.
asyncio.set_event_loop(self.loop)
asyncio.get_event_loop().create_task(self._mirror_stream(self.data_stream))
asyncio.ensure_future(self._serve(self.ssl_context))enter code here
Ignore the indent issue, SO wont indent correctly.
And here is the method that is failing with the error 'Task was destroyed but it is pending!'. Keep in mind, if I do not include the lines with 'data_stream.get()' the function runs fine. I made sure, the objects in both locations have the same memory address AND value for id(). If i print the data that comes from the await self.data_stream.get() I get the correct data. However after that it seems to just return and break. Here is the code:
async def _mirror_stream(self):
while True:
stream_length = self.data_stream.length
try:
if stream_length > 1:
for _ in range(0, stream_length):
data = await self.data_stream.get()
else:
data = await self.data_stream.get()
except Exception as e:
print(str(e))
# If the data is null, keep the last known value
if self._is_json_serializable(data) and data is not None:
self.payload = json.dumps(data)
else:
RUNTIME_LOGGER.warning(
"Mirroring stream encountered a Null payload in WebsocketProducer!")
await asyncio.sleep(self.poll_rate)enter code here
The issue has been resolved by implementing my own async Queue by utilizing the normal queue.Queue object. For some reason the application would only work if I would 'await' for queue.get(), even though it wasnt an asyncio.Queue object... Not entirely sure why this behavior occured, however the application is running well, and still performing as if the Queue were from the asyncio lib. Thanks to those who looked!

Why is this exception immediately raised from an asyncio Task?

My understanding from the documentation is that asyncio.Tasks, as an asyncio.Future subclass, will store exceptions raised in them and they can be retrieved at my leisure.
However, in this sample code, the exception is raised immediately:
import asyncio
async def bad_task():
raise Exception()
async def test():
loop = asyncio.get_event_loop()
task = loop.create_task(bad_task())
await task
# I would expect to get here
exp = task.exception()
# but we never do because the function exits on line 3
loop = asyncio.get_event_loop()
loop.run_until_complete(test())
loop.close()
Example output (Python 3.6.5):
python3 ./test.py
Traceback (most recent call last):
File "./test.py", line 15, in <module>
loop.run_until_complete(test())
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py", line 468, in run_until_complete
return future.result()
File "./test.py", line 9, in test
await task
File "./test.py", line 4, in bad_task
raise Exception()
Exception
Is this a quirk of creating & calling tasks when already within async code?
await will raise any exception thrown by the task, because it's meant to make asynchronous code look almost exactly like synchronous code. If you want to catch them, you can use a normal try...except clause.
As Matti explained, exceptions raised by a coroutine are propagated to the awaiting site. This is intentional, as it ensures that errors do not pass silently by default. However, if one needs to do so, it is definitely possible to await a task's completion without immediately accessing its result/exception.
Here is a simple and efficient way to do so, by using a small intermediate Future:
async def test():
loop = asyncio.get_event_loop()
task = loop.create_task(bad_task())
task_done = loop.create_future() # you could also use asyncio.Event
# Arrange for task_done to complete once task completes.
task.add_done_callback(task_done.set_result)
# Wait for the task to complete. Since we're not obtaining its
# result, this won't raise no matter what bad_task() does...
await task_done
# ...and this will work as expected.
exp = task.exception()

Resources