首页 > 解决方案 > RuntimeError:在 Python 中使用 Celery 和 Flask 在应用程序上下文之外工作

问题描述

我已经阅读了许多不同的博客来解释如何结合 Flask 和 Celery。我还阅读了大量关于该主题的堆栈问题。但是,已经好几天了,我仍然无法解决这个“RuntimeError:在应用程序上下文之外工作”,我不知道此时我能做些什么来解决这个问题。

该项目是这样完成的:

我实现了我的项目以拥有本文中所做的工厂架构。然后,如本文所述,已完成 celery 实现以适应该工厂架构。

HTTP 请求在 routes.py 中处理,并将调用 celery 任务在后台完成。同时,应用程序可以继续运行并做其他事情。它将发送一个带有另一个函数的 POST HTTP,当 celery 任务完成后,它将发送一个 celery 结果的 POST HTTP。

routes.py 包含以下内容,我已将其简化为重要部分:

from flask import request, make_response, jsonify, copy_current_request_context
from flask import current_app as app
from application import tfidf_matching
import time
from application.models import db, Status
from app import cel

def snooze(maxTime):
    ...


@app.before_first_request
def default_values():
    ...

@cel.task
def tfidf(question):
    with app.app_context:
        answer = tfidf_matching.getMatchingSentence(question)
        ...
        return answer

@app.route('/webhook', methods=['GET', 'POST'])
def webhook():
    req = request.get_json(force=True)
    ...
    if ... :
        user_input = req.get('queryResult').get('queryText')
        answer = tfidf.apply_async(args=[user_input], expires=60)
        ...
    snooze(4)
    response = {'followupEventInput': {'name': 'snooze'}}
    if ...:
        response = {'fulfillmentText': answer}
    return make_response(jsonify(response))

我尝试使用 @copy_current_request_context 和 app.app_context 来解决其他堆栈溢出问题中建议的错误,但没有成功。

使用以下命令启动 Celery:

celery worker -A celery_worker.cel --loglevel=info

celery_worker.py 包含以下内容:

import os
from app.routes import tfidf
from application import cel, create_app

app = create_app(os.getenv('FLASK_CONFIG') or 'default')
app.app_context().push()

我不确定是否有必要,但如果我还要添加 init 和 wsgi 内容。Models 是一个 SQLAlchemy 数据库,这似乎不相关,所以我不会添加这部分。

__ init__.py 包含以下内容:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from celery import Celery
from config import config, Config

db = SQLAlchemy()

cel = Celery(__name__, broker=Config.broker_url, backend=Config.result_backend)


def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    db.init_app(app)
    app.config.update(
        broker_url='redis://localhost:6379',
        result_backend='redis://localhost:6379',
        SQLALCHEMY_DATABASE_URI='postgresql://postgres:APG@localhost:5432/Dialogflow',
        SQLALCHEMY_TRACK_MODIFICATIONS='None'
    )
    cel.conf.update(
        result_expires=3600,
    )
    cel.conf.update(app.config)
    with app.app_context():
        from . import routes
        db.create_all()
        return app

wsgi.py 包含以下内容:

import os
from application import create_app

app = create_app(os.getenv('FLASK_CONFIG') or 'default')

if __name__ == "__main__":
    app.run(host='0.0.0.0')

最后,这是堆栈跟踪:

    Traceback (most recent call last):
  File "c:\users\emma\appdata\local\programs\python\python37\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Users\Emma\AppData\Local\Programs\Python\Python37\Scripts\celery.exe\__main__.py", line 7, in <module>
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\__main__.py", line 16, in main
    _main()
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\celery.py", line 322, in main
    cmd.execute_from_commandline(argv)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\celery.py", line 495, in execute_from_commandline
    super(CeleryCommand, self).execute_from_commandline(argv)))
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\base.py", line 289, in execute_from_commandline
    argv = self.setup_app_from_commandline(argv)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\base.py", line 509, in setup_app_from_commandline
    self.app = self.find_app(app)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\base.py", line 531, in find_app
    return find_app(app, symbol_by_name=self.symbol_by_name)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\app\utils.py", line 373, in find_app
    sym = symbol_by_name(app, imp=imp)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\bin\base.py", line 534, in symbol_by_name
    return imports.symbol_by_name(name, imp=imp)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\kombu\utils\imports.py", line 57, in symbol_by_name
    module = imp(module_name, package=package, **kwargs)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\celery\utils\imports.py", line 111, in import_from_cwd
    return imp(module, package=package)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\importlib\__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\celery_worker.py", line 2, in <module>
    from application.routes import tfidf
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\application\routes.py", line 21, in <module>
    @app.before_first_request
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\werkzeug\local.py", line 348, in __getattr__
    return getattr(self._get_current_object(), name)
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\werkzeug\local.py", line 307, in _get_current_object
    return self.__local()
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\flask\globals.py", line 52, in _find_app
    raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.

This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context().  See the
documentation for more information.

编辑

通过按照 Miguel 的建议进行更改from flask import current_app as app,错误消失了。

我将导入更改为from wsgi import app在 routes.py 中,但这不起作用。我的应用程序实例应该以另一种方式导入,但我不知道如何

使用以下命令运行烧瓶时: flask run 我得到以下 Traceback

Traceback (most recent call last):
  File "c:\users\emma\appdata\local\programs\python\python37\lib\site-packages\flask\cli.py", line 240, in locate_app
    __import__(module_name)
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\wsgi.py", line 4, in <module>
    app = create_app(os.getenv('FLASK_CONFIG') or 'default')
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\application\__init__.py", line 28, in create_app
    from . import routes
  File "C:\Users\Emma\PycharmProjects\APG_Dialogflow\application\routes.py", line 6, in <module>
    from wsgi import app
ImportError: cannot import name 'app' from 'wsgi' (C:\Users\Emma\PycharmProjects\APG_Dialogflow\wsgi.py)

标签: pythonflaskruntime-errorcelerywebhooks

解决方案


问题是你的代码中有这个:

from flask import current_app as app

然后你使用appin@app.route@app.before_first_request

这是不正确的,Flask 无法弄清楚你的应用程序实例是什么。您需要做的是使用真实的应用程序实例而不是current_app.

您可以current_app在视图函数中使用,因为 Flask 为您设置了应用程序上下文,但您不能在应用程序的全局范围内使用它。

更新后更新答案:

你现在怎么一个循环依赖问题。注意最后一个堆栈跟踪是如何显示的wsgi.py,进入application/__init__.py,然后到application/routes.py最后返回到wsgi.py

在 Flask 应用程序中解决此问题的一个相当常见的解决方案是将from . import routes文件application/__init__.py中的 . 文件的底部。我相信这将消除循环依赖。


推荐阅读