RuntimeError: Работа вне контекста приложения с Celery и Flask in Python - PullRequest
0 голосов
/ 21 января 2020

Я прочитал много разных блогов, объясняющих, как объединить Flask и сельдерей. Я также прочитал множество вопросов по этой теме. Однако прошли дни, и мне все еще не удается решить эту «RuntimeError: Работа вне контекста приложения», и я не знаю, что я могу сделать, чтобы решить эту проблему на данный момент.

Проект выполняется так:

  • / application

    • __ init __
    • модели
    • маршруты
    • tfidf_matching
  • celery_worker
  • config
  • wsgi

Я реализовал свой проект, чтобы иметь архитектуру Factory, как это было сделано на это статья . Затем была реализована реализация сельдерея, чтобы соответствовать архитектуре Factory, как описано в этой статье 1030 *.

HTTP-запрос обрабатывается вways.py и вызовет задачу сельдерея, которая будет выполняться в фоновом режиме. Тем временем приложение может продолжать работать и делать другие вещи. Он отправит POST HTTP с другой функцией, и когда задача сельдерея будет выполнена, он отправит POST HTTP результата сельдерея.

rout.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.

Редактировать

При изменении from flask import current_app as app, как предложено Мигелем, Ошибка исчезает.

Я изменил импорт на from wsgi import app в rout.py, но это не работает. Мой экземпляр приложения должен быть импортирован другим способом, но я не знаю, как

При запуске flask с: flask run я получаю следующую трассировку

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)

1 Ответ

0 голосов
/ 21 января 2020

Проблема в том, что у вас есть это в вашем коде:

from flask import current_app as app

А затем вы используете app в @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 до конца. нижняя часть файла. Я верю, что это устранит круговую зависимость.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...