Error of coroutine of asyncio python package - python

I am not able to call Async function inside another async function. Shows RuntimeError.
RuntimeWarning: coroutine 'on_get' was never awaited
handle = None # Needed to break cycles when an exception occurs.
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
So I have developed a CRUD operation API in newly python framework called Robyn. I have Developed four endpoints for all four operations where I can retrieve, create, update and delete and one endpoint which can perform all four together.
So Robyn supports asynchronous functions. I am trying to call each function inside the function which can perform all operations. But instead, I am getting RuntimeError.
#app.get("/") # Retrieve
async def on_get(request):
result = await asyncio.create_task(Retrieve())
return result
#app.get("/:opt/values")
async def on_dynm(request):
que = request['queries']
opt = str.lower(request['params']['opt'])
if opt == "create":
result = asyncio.run(on_post(request))
elif opt == "update":
name = que['name']
NewName = que['NewName']
result = asyncio.run(on_put(name,NewName))
elif opt == "delete":
name = que['name']
result = asyncio.run(on_delete(request))
else:
result = await asyncio.run(on_get(request))
return result

Related

Retry logic being ignored for server errors with Google Cloud Text-to-speech API

I'm trying to use Google Cloud's text-to-speech API. The problem I'm running into is that periodically the API returns a status of "500 Internal Server Error." The correct logic for these errors is usually to just retry the call. Unfortunately, I can't get any of Google Cloud's retry logic to work. As soon as I hit the exception my script exits.
My API function:
async def get_audio_from_google(input_text: str, output_file: str):
"""
Convert the provided text to audio using the Google text-to-speech API.
Args:
input_text: Text to conver to speech.
output_file: File path to write. File extension will be added automatically.
Returns: Writes the audio file to disk. Does not return a result.
"""
client = texttospeech.TextToSpeechAsyncClient()
# Create and configure the Synthesis object.
synthesis_input = texttospeech.SynthesisInput()
synthesis_input.text = input_text
voice_parameters = texttospeech.VoiceSelectionParams()
voice_parameters.language_code = VOICE_ENCODING
voice_parameters.name = VOICE
audio_parameters = texttospeech.AudioConfig()
if AUDIO_FORMAT == AudioFormat.MP3:
audio_parameters.audio_encoding = texttospeech.AudioEncoding.MP3
elif AUDIO_FORMAT == AudioFormat.OPUS:
audio_parameters.audio_encoding = texttospeech.AudioEncoding.OGG_OPUS
else:
print("Invalid audio format specified")
sys.exit(1)
logging.info(f"Synthesizing speech for {output_file}")
# Build our request.
request = texttospeech.SynthesizeSpeechRequest()
request.input = synthesis_input
request.voice = voice_parameters
request.audio_config = audio_parameters
# Get audio.
# Configure when to retry on error.
retry_object = retry.Retry(initial=5, timeout=90)
response = await client.synthesize_speech(request=request, retry=retry_object)
with open(f"{output_file}.{AUDIO_FORMAT}", "wb") as out:
# Write the response to the output file.
out.write(response.audio_content)
logging.info(f'Audio content written to file "{output_file}.{AUDIO_FORMAT}"')
TextToSpeechAsyncClient's synthesize_speech method accepts an instance of Retry, which is part of the Google Core API and can be used as a decorator or passed to some methods. Unfortunately, I can't seem to get the retry logic to work. By default it should retry on any error classed as transient, which includes Internal Server Error (error 500):
if_transient_error = if_exception_type(
exceptions.InternalServerError,
exceptions.TooManyRequests,
exceptions.ServiceUnavailable,
requests.exceptions.ConnectionError,
requests.exceptions.ChunkedEncodingError,
auth_exceptions.TransportError,
I've tried both passing retry to synthesize_speech and using it as a decorator for get_audio_from_google. In either case, as soon as my script gets an error response from the server, it exits.
How I'm calling get_audio_from_google:
def process_audio(text: List[str]):
"""
Process text asynchronously in segments and output a bunch of audio files ready for stitching.
Args:
text (List[str]): List of text snippets to process.
"""
async def gather_with_concurrency(max_tasks: int, *coroutines):
"""
Run tasks in parallel with a limit.
https://stackoverflow.com/questions/48483348/how-to-limit-concurrency-with-python-asyncio
Args:
max_tasks: Maximum number of tasks to run at once.
*coroutines: All async tasks that should be run.
"""
semaphore = asyncio.Semaphore(max_tasks)
async def sem_coro(coro):
async with semaphore:
return await coro
return await asyncio.gather(*(sem_coro(c) for c in coroutines))
async def main():
snippet_counter = 1
subtasks = []
for text_snippet in text:
snippet_filename = str(snippet_counter)
snippet_counter += 1
subtasks.append(
get_audio_from_google(input_text=text_snippet, output_file=snippet_filename)
)
await gather_with_concurrency(2, *subtasks)
logging.info("Starting audio processing tasks…")
# Begin execution.
asyncio.run(main())

Python, sending true value from one script to another while using for loop

I would like to delete a blob(file) only if successfully saved to azure and not do anything if not deleted. I'm trying to implement this with the below structure.
function archival runs function tasks where each work in works is attempted to be uploaded by using function upload if uploaded successfully, send True parameter to upload success.
back to function archival, after script2.task is done, run a for loop that checks if work is successfully uploaded by checking if upload success has true value. if so, delete the given work from database.
would this be a possible or am I getting something wrong. How would I send true value for each independent work in works so that it checks in Script 1.
#Script 1
async def archival():
await script2.task(works)
for work in works:
# implement if statment here
upload Script2.upload_success:
await gwm.delete_work(work["id"])
#Script 2
async def upload():
blob_client.upload_blob(content.encode())
upload_success(True)
async def task():
try:
for work in works:
try:
await upload()
except Exception:
return False
except Exception as ex:
logging.exception(ex)
async def upload_success():
return True
One possible solution would be to return a dictionary from task method. The key for each item in that dictionary would be the work identifier and the value would be either True or False based on the result.
This would mean your upload method would work with a single work item and would return True or False.
Then in your archival method, you can simply loop over this dictionary and decide whether to delete the work item or not based on the value for each key in that dictionary.
UPDATE
Please see the pseudo code:
Script 2
async def upload(work):
blob_client.upload_blob(content.encode())
upload_success(True)
async def task():
result = {} #this is the dictionary that will be returned.
for work in works:
try:
await upload(work)
result["work-id"] = True
except Exception:
result["work-id"] = False
return result
async def upload_success():
return True
Script 1
async def archival():
result = await script2.task(works)
for key in result:
is_successful = result[key]
if is_successful:
await gwm.delete_work(key)

asyncio run_until_complete does not wait that all coroutines finish

I am making my first steps in Python and I have a bit of struggle trying to understand why I do not have the expected result with this one. Here is what I am trying to achieve :
I have a function that consumes an API. While waiting for the API to answer and given that I am going through a proxy that creates additional lag, I though that sending concurrent request will speed up the process (I run 100 concurrent requests). It does. But asyncio run_until_complete always returns some unfinished coroutines.
Here the code (simplified):
import aiohttp
import asyncio
async def consume_api(parameter):
url = "someurl" #it is actually based on the parameter
try:
async with aiohttp.ClientSession() as session:
async with session.get(URL, proxy="someproxy") as asyncresponse:
r = await asyncresponse.read()
except:
global error_count
error_count += 1
if error_count > 50:
return "Exceeded 50 try on same request"
else:
return consume_api(parameter)
return r.decode("utf-8")
def loop_on_api(list_of_parameter):
loop = asyncio.get_event_loop()
coroutines = [consume_api(list_of_parameter[i]) for i in range(len(list_of_parameter))]
results = loop.run_until_complete(asyncio.gather(*coroutines))
return results
When I run the debugger, the results returned by the loop_on_api function include a list of string corresponding to the results of consume_api and some occurence of <coroutine objects consume_api at 0x00...>. Those variables have a cr_running parameter at False and a cr_Frame.
Though if I check the coroutines variables, I can find all the 100 coroutines but none seems to have a cr_Frame.
Any idea what I am doing wrong?
I'm also thinking my way of counting the 50 error will be shared by all coroutines.
Any idea how I can make it specific?
This should work, you can add/change/refactor what ever you want
import aiohttp
import asyncio
async def consume_api(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.read()
def loop_on_api(list_of_urls):
loop = asyncio.get_event_loop()
coroutines = [consume_api(url) for url in list_of_urls]
results = loop.run_until_complete(asyncio.gather(*coroutines))
return results
if __name__ == '__main__':
print(loop_on_api(['https://google.com', 'https://twitter.com']))
It seems the issue is coming from the proxy I am using, which sometimes do not carry the request or response. Hence forcing a rerun seems to be the best answer. Hence I now check if the results returned have some coroutines remaining and re-run the loop_on_api() on them
def loop_on_api(list_of_parameter):
loop = asyncio.get_event_loop()
coroutines = [consume_api(list_of_parameter[i]) for i in range(len(list_of_parameter))]
results = loop.run_until_complete(asyncio.gather(*coroutines))
undone = []
rerun_list_of_parameter = []
for i in range(len(results)):
if str(type(results[i])) == "<class 'coroutine'>": #not very elegant >> is there a better way?
undone.append(i)
rerun_list_of_parameter.append(list_of_parameter[i])
if len(undone) > 0:
undone_results = loop_on_api(rerun_list_of_parameter)
for i in range(len(undone_results)):
results[undone[i]] = undone_results[i]
return results

Sharing an aiohttp.ClientSession between multiple asynchronous callers

The goal
I want to make regular HTTP requests to a REST API. The requests happen at an interval ranging from a few seconds up to multiple hours, depending on the user input. I want to keep a single connection alive and close it in a smart way.
The questions
Is there an existing library that provides features similar to what i wrote with the class SessionOneToN?
Would it be possible to use a single session of type aiohttp.ClientSession that runs forever and is never closed?
What i have tried
What i have tried does work, but i wonder if there is an established library that can achieve the goals.
My code
My application uses the event loop module asyncio, and the HTTP module aiohttp.
My application opens a single session of type aiohttp.ClientSession and shares it between multiple asynchronous callers.
The callers can make their requests concurrently and asynchronously.
Whenever all callers have received their responses at the same time, the aiohttp.ClientSession is closed. A new aiohttp.ClientSession is opened as necessary when a caller makes a new request.
import aiohttp
import asyncio
from contextlib import asynccontextmanager
import sys
url = 'http://stackoverflow.com/questions/64305548/sharing-an-aiohttp-clientsession-between-multiple-asynchronous-callers'
# The callers look like so
async def request():
async with session() as s:
async with s.get(url) as response:
return await response.text()
# The session manager looks like so:
class SessionOneToN:
""" Manage one session that is reused by multiple clients, instantiate a new
session object from the given session_class as required. Provide a context
manager for accessing the session. The session_class returns a context
manager when instantiated, this context manager is referred to as the
internal context manager, and is entered when the first
client enters the context manager returned by context_manager, and is exited
when the last client exits context_manager."""
def __init__(self, session_class):
self.n = 0
self.session_class = session_class
self.context = None
self.aenter_result = None
async def plus(self):
self.n += 1
if self.n == 1:
# self.context: The internal context manager
self.context = context = self.session_class()
# self.aenter_result: The result from entering the internal context
# manager
self.aenter_result = await context.__aenter__()
assert self.aenter_result is not None
return self.aenter_result
def minus(self):
self.n -= 1
if self.n == 0:
return True
def cleanup(self):
self.context = None
self.aenter_result = None
#asynccontextmanager
async def get_context(self):
try:
aenter_result = await self.plus()
yield aenter_result
except:
if self.minus():
if not await self.context.__aexit__(*sys.exc_info()):
self.cleanup()
raise
self.cleanup()
else:
if self.minus():
await self.context.__aexit__(None, None, None)
self.cleanup()
class SessionOneToNaiohttp:
def __init__(self):
self.sotn = SessionOneToN(aiohttp.ClientSession)
def get_context(self):
return self.sotn.get_context()
sotnaiohttp = SessionOneToNaiohttp()
def session():
return sotnaiohttp.get_context()
response_text = asyncio.run(request())
print(response_text[0:10])

How to implement a async grpc python server?

I need to call a celery task for each GRPC request, and return the result.
In default GRPC implementation, each request is processed in a separate thread from a threadpool.
In my case, the server is supposed to process ~400 requests in batch mode per second. So one request may have to wait 1 second for the result due to the batch processing, which means the size of the threadpool must be larger than 400 to avoid blocking.
Can this be done asynchronously?
Thanks a lot.
class EventReporting(ss_pb2.BetaEventReportingServicer, ss_pb2.BetaDeviceMgtServicer):
def ReportEvent(self, request, context):
res = tasks.add.delay(1,2)
result = res.get() ->here i have to block
return ss_pb2.GeneralReply(message='Hello, %s!' % result.message)
As noted by #Michael in a comment, as of version 1.32, gRPC now supports asyncio in its Python API. If you're using an earlier version, you can still use the asyncio API via the experimental API: from grpc.experimental import aio. An asyncio hello world example has also been added to the gRPC repo. The following code is a copy of the example server:
import logging
import asyncio
from grpc import aio
import helloworld_pb2
import helloworld_pb2_grpc
class Greeter(helloworld_pb2_grpc.GreeterServicer):
async def SayHello(self, request, context):
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
async def serve():
server = aio.server()
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
listen_addr = '[::]:50051'
server.add_insecure_port(listen_addr)
logging.info("Starting server on %s", listen_addr)
await server.start()
await server.wait_for_termination()
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
asyncio.run(serve())
See my other answer for how to implement the client.
It can be done asynchronously if your call to res.get can be done asynchronously (if it is defined with the async keyword).
While grpc.server says it requires a futures.ThreadPoolExecutor, it will actually work with any futures.Executor that calls the behaviors submitted to it on some thread other than the one on which they were passed. Were you to pass to grpc.server a futures.Executor implemented by you that only used one thread to carry out four hundred (or more) concurrent calls to EventReporting.ReportEvent, your server should avoid the kind of blocking that you describe.
In my opinion is good simple implementation async grpc server, same like http based on aiohttp.
import asyncio
from concurrent import futures
import functools
import inspect
import threading
from grpc import _server
def _loop_mgr(loop: asyncio.AbstractEventLoop):
asyncio.set_event_loop(loop)
loop.run_forever()
# If we reach here, the loop was stopped.
# We should gather any remaining tasks and finish them.
pending = asyncio.Task.all_tasks(loop=loop)
if pending:
loop.run_until_complete(asyncio.gather(*pending))
class AsyncioExecutor(futures.Executor):
def __init__(self, *, loop=None):
super().__init__()
self._shutdown = False
self._loop = loop or asyncio.get_event_loop()
self._thread = threading.Thread(target=_loop_mgr, args=(self._loop,),
daemon=True)
self._thread.start()
def submit(self, fn, *args, **kwargs):
if self._shutdown:
raise RuntimeError('Cannot schedule new futures after shutdown')
if not self._loop.is_running():
raise RuntimeError("Loop must be started before any function can "
"be submitted")
if inspect.iscoroutinefunction(fn):
coro = fn(*args, **kwargs)
return asyncio.run_coroutine_threadsafe(coro, self._loop)
else:
func = functools.partial(fn, *args, **kwargs)
return self._loop.run_in_executor(None, func)
def shutdown(self, wait=True):
self._loop.stop()
self._shutdown = True
if wait:
self._thread.join()
# --------------------------------------------------------------------------- #
async def _call_behavior(rpc_event, state, behavior, argument, request_deserializer):
context = _server._Context(rpc_event, state, request_deserializer)
try:
return await behavior(argument, context), True
except Exception as e: # pylint: disable=broad-except
with state.condition:
if e not in state.rpc_errors:
details = 'Exception calling application: {}'.format(e)
_server.logging.exception(details)
_server._abort(state, rpc_event.operation_call,
_server.cygrpc.StatusCode.unknown, _server._common.encode(details))
return None, False
async def _take_response_from_response_iterator(rpc_event, state, response_iterator):
try:
return await response_iterator.__anext__(), True
except StopAsyncIteration:
return None, True
except Exception as e: # pylint: disable=broad-except
with state.condition:
if e not in state.rpc_errors:
details = 'Exception iterating responses: {}'.format(e)
_server.logging.exception(details)
_server._abort(state, rpc_event.operation_call,
_server.cygrpc.StatusCode.unknown, _server._common.encode(details))
return None, False
async def _unary_response_in_pool(rpc_event, state, behavior, argument_thunk,
request_deserializer, response_serializer):
argument = argument_thunk()
if argument is not None:
response, proceed = await _call_behavior(rpc_event, state, behavior,
argument, request_deserializer)
if proceed:
serialized_response = _server._serialize_response(
rpc_event, state, response, response_serializer)
if serialized_response is not None:
_server._status(rpc_event, state, serialized_response)
async def _stream_response_in_pool(rpc_event, state, behavior, argument_thunk,
request_deserializer, response_serializer):
argument = argument_thunk()
if argument is not None:
# Notice this calls the normal `_call_behavior` not the awaitable version.
response_iterator, proceed = _server._call_behavior(
rpc_event, state, behavior, argument, request_deserializer)
if proceed:
while True:
response, proceed = await _take_response_from_response_iterator(
rpc_event, state, response_iterator)
if proceed:
if response is None:
_server._status(rpc_event, state, None)
break
else:
serialized_response = _server._serialize_response(
rpc_event, state, response, response_serializer)
print(response)
if serialized_response is not None:
print("Serialized Correctly")
proceed = _server._send_response(rpc_event, state,
serialized_response)
if not proceed:
break
else:
break
else:
break
_server._unary_response_in_pool = _unary_response_in_pool
_server._stream_response_in_pool = _stream_response_in_pool
if __name__ == '__main__':
server = grpc.server(AsyncioExecutor())
# Add Servicer and Start Server Here
link to original:
https://gist.github.com/seglberg/0b4487b57b4fd425c56ad72aba9971be

Categories