Comments (12)
This is a common issue with Flask and Quart contexts, you can solve it via the copy_current_app_context
decorator, as so,
Thread(
target=copy_current_app_context(send_async_email),
args=(current_app._get_current_object(), msg),
).start()
The issue is that the context (with Quart 0.7 onwards) is copied for you into different asyncio tasks, however it isn't copied into a different Thread. The explicit usage of the copy_current_app_context
does so though.
from quart.
I am getting the following warning:
RuntimeWarning: coroutine 'wrapper' was never awaited
self._target(*self._args, **self._kwargs)
The wrapped function does not run either.
from quart.
Ah yes as the copy_current_app_context
returns a coroutine function rather than a function. You need to run an event loop on the other thread.
Are you trying to run a sync or async function in another thread? Also are you trying to invoke the thread from a sync or async context?
from quart.
Sync in the Thread as shown in the example above. I am invoking from an async context within one of the Quart_OpenAPI endpoints:
def send_email(_app, message):
with _app.app_context():
mail.send(message)
@app.route('/test')
class Test(Resource):
async def get(self):
message = Message(sender=app.config.get('MAIL_USERNAME'),
recipients=[app.config.get('MAIL_USERNAME')],
subject='TEST')
Thread(
target=copy_current_app_context(send_email),
args=(
current_app._get_current_object(),
message
)
).start()
response = {
'message': 'New registration email sent.'
}
return jsonify(response), 200
from quart.
Additionally, this is what I tried to run an event loop on the other thread, but I am still getting the same warning:
def send_email(_app, _loop, message):
with _app.app_context():
print('====> sending on a separate thread.')
asyncio.set_event_loop(_loop)
_loop.run_until_complete(
mail.send(message)
)
@app.route('/test')
class Test(Resource):
async def get(self):
message = Message(sender=app.config.get('MAIL_USERNAME'),
recipients=[app.config.get('MAIL_USERNAME')],
subject='TEST')
thr = Thread(
target=copy_current_app_context(send_email),
args=(
current_app._get_current_object(),
asyncio.new_event_loop(),
message
)
).start()
response = {
'message': 'New registration email sent.'
}
return jsonify(response), 200
from quart.
Sorry to spam, this technique has failed me as well: https://pgjones.gitlab.io/quart/background_tasks.html
from quart.
Does this work?
from contextvars import copy_context
from functools import partial
async def run_sync(func) -> Any:
loop = asyncio.get_running_loop()
return await loop.run_in_executor(
None, copy_context().run, func,
)
def send_email(message):
with app.app_context():
print('====> sending on a separate thread.')
mail.send(message)
@app.route('/test')
async def get(self):
message = Message(
sender=app.config.get('MAIL_USERNAME'),
recipients=[app.config.get('MAIL_USERNAME')],
subject='TEST',
)
await run_sync(partial(send_email, message))
If so it will get a lot easier in the next Quart release (if not I need to do some more work).
from quart.
Unfortunately, I get the following:
File "app.py", line 100, in send_email
with app.app_context:
AttributeError: __enter__
from quart.
Hmm tricky, this then needs to allow the execution of sync code in a thread surrounded by an async context setup. Does this work?
from contextvars import copy_context
from functools import partial
async def run_sync(func) -> Any:
loop = asyncio.get_running_loop()
return await loop.run_in_executor(
None, copy_context().run, func,
)
def send_email(message):
print('====> sending on a separate thread.')
mail.send(message)
@app.route('/test')
async def get(self):
message = Message(
sender=app.config.get('MAIL_USERNAME'),
recipients=[app.config.get('MAIL_USERNAME')],
subject='TEST',
)
async with app.app_context():
await run_sync(partial(send_email, message))
I've wrapped the run sync in the app context setup, which should keep it set during the run_sync
execution. As run_sync copies the context it should also be available in the send_email
function...
from quart.
This works!
Pardon my confusion though, I was under the impression that the intent of the original post was to create a function on a new thread to provide the end user with a response while dealing with the mail server connection asynchronously. With this fix it is still blocking my response. Maybe the confusion stemmed from my choice to make the send mail function synchronous, though I kept getting a never awaited error when attaching it to the thread.
from quart.
Ah, the above solution ensures that the actual sending of the message doesn't block the event loop thereby allowing the app/server to serve other requests concurrently. To achieve a background send you will need to use a background task as well,
from contextvars import copy_context
from functools import partial
async def run_sync(func) -> Any:
loop = asyncio.get_running_loop()
return await loop.run_in_executor(
None, copy_context().run, func,
)
def send_email(message):
print('====> sending on a separate thread.')
mail.send(message)
@app.route('/test')
async def get():
async def _get():
message = Message(
sender=app.config.get('MAIL_USERNAME'),
recipients=[app.config.get('MAIL_USERNAME')],
subject='TEST',
)
async with app.app_context():
await run_sync(partial(send_email, message))
asyncio.ensure_future(_get())
return "Pending", 201
from quart.
This above code is perfect! Thank you so much for the help.
from quart.
Related Issues (20)
- Remove poetry from examples in documentation HOT 2
- Allow kwargs in Quart.test_client for custom test client implementations HOT 1
- unix socket support HOT 1
- reload() puts comman name after command line arguments HOT 2
- Hot reload does not work within poetry scripts HOT 2
- New doc contribution: Add a doc to integrate Quart with BentoML HOT 1
- The parameter of ContentRange in quart.wrappers.Response._process_range_request is not correct.
- Handling CTRL+C for websockets [linux] HOT 2
- 'quart.app' vs 'app' for log level configuration via dictConfig
- [Docs] AttributeError: 'async_generator' object has no attribute 'test_client'
- after_this_websocket is not called if the client disconnects?
- Background tasks can disappear mid-execution HOT 1
- Server sent events guide: Add = None to optional retry argument
- Cannot get request files with NGINX HOT 1
- Hot-reload not working on paths with spaces
- Blog tutorial has typo for path
- Support Accept-Range header in send_file?
- Hot reload cause a permission denied error in docker container HOT 2
- asyncio error in quart/wrappers/request.py HOT 3
- Quart test client does not support same "data" arguments as Flask / Werkzeug
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from quart.