flask-mail
flask-mail copied to clipboard
RuntimeError: working outside of application context
When using the mailer to send an e-mail from a background worker (that is, outside of the Flask application context), it crashes and burns.
This is expected. Flask-Mail requires an application context so that it can read the configuration values. There are two options:
- Customize your background worker(s) to create an application context before each task is executed
- Create an application context in your task. For example:
@task
def send_email(msg):
with app.app_context():
mail.send(msg)
You were quick to close it. I will argue that this is neither expected nor sensible. Flask-mail requires the app object to read configuration values when it is initialized, not at any point in time later. There is only a signal that updates the application on sent e-mail, but that is optional behavior.
After initialization, flask-mail is just a mailer. Instead of littering application code with app.app_context()
, I think this should be accommodated by the library.
I was quick to close, I apologize. I see your point now, the code can be adjusted to work outside an app context. However, I think it depends if you use the init_app
function or not. At which point the current_app
object would have to be used to read configuration values. If you happen to use Mail(app)
all will be good.
I will give a pull-request a shot. Unless I am mistaken about Flask internals and looking into the Mail code, both init_app
and the Mail()
constructor only access the app.config
and do not require a current_app
to be present.
Hi guys, any suggestion about this issue? using Mail(app)
i still got runtime error "working outside of application context"
@dtheodor if I recall correctly, this issue is more about Flask itself, not flask-mail.
@mattupstate with app.app_context():
works just fine, but it's a bit annoying to write it over and over again.
If I'm not mistaken, this problem appears because there is no actual current_app when you are using a background worker.
Potential workaround is to to use self.app.app_context()
within send function.
What do you think?
I forgot to mention that here should be self.app = app
as well.
I think I'd like to step back and remind everyone that this is a Flask extension. It's designed to be used with Flask. That means the extension must consider the possibility of multiple apps running in the same process. If you're looking for something less tied to Flask you might want to look into other libraries such as Envelopes. You could easily write the glue yourself if you need to tie configuration options on your application to a mail client.
@mattupstate shouldn't one register Mail
instance for each app? In this case my solution should work (I guess).
At least the section about flask-mail, background workers and possible use of with app.app_context(): mail.send(msg)
should be added somewhere in the docs.
@jackunion you could do that if you like, but Flask extensions should be designed to support multiple apps via the init_app
method.
@mattupstate I understand you perfectly. Anyway, it is your decision to merge or not to merge that matters, but I don't see how self.app = app
could break support for multiple apps (there already exists self.app = app
inside Mail.__init__
but it is never used).
Sent you a PR.
@jackunion Please see http://flask.pocoo.org/docs/extensiondev/, init_app
should not bind the extension object to a specific app.
@untitaker thank you for making this clear.
could this be done with from flask import current_app
?
No, current_app
requires an app context.
OK, I'm "dealing with it" and use with app.app_context(): .....
But why is it that DEFAULT_MAIL_SENDER in settings.py does not get recognized:
File "C:\Anaconda\lib\site-packages\flask_mail.py", line 156, in send "The message does not specify a sender and a default sender " AssertionError: The message does not specify a sender and a default sender has not been configured
That's a totally different issue, but the setting is MAIL_DEFAULT_SENDER
.
Due to the requirement "one Mail
instance and multiple Flask
apps all configured through the Mail
instance's init_app
", I don't think what I had in mind is possible. The Mail
object then always needs the current_app
to figure out which app (aka mail configuration) it should use. If it is instantiated through Mail(app)
, it already works without requiring the current_app
to be set. You can close the issue as cannot fix
as far as I am concerned.
After six years, But yeah i hope this might hint for other developers.
Thread( target=copy_current_app_context(send_async_email), args=(current_app._get_current_object(), msg), ).start()
We need to use get_current_object().
https://github.com/pgjones/quart/issues/56
You must be inside a context to access current_app
. Either you're in a request or a CLI command, in which case this is already true, or you push one manually with app.app_context():
. This is a general pattern in Flask and extensions.