Интеграция Celery с Flask с использованием фабричного шаблона приложения: ошибка максимальной глубины рекурсии - PullRequest
0 голосов
/ 24 февраля 2019

Я работаю с шаблоном Flask cookiecutter, который использует шаблон фабрики приложений.Celery работал для задач, которые не использовали контекст приложения, но одна из моих задач должна знать это;он делает запрос к базе данных и обновляет объект базы данных.Прямо сейчас у меня не круговая ошибка импорта (хотя они были с другими попытками), а ошибка максимальной глубины рекурсии.

Я проконсультировался в этом блоге о том, как использовать Celery с шаблоном фабрики приложений, и я стараюсь внимательно следить за этим ответом переполнения стека , так как он имеетструктура, по-видимому, также получена из Flask.

Соответствующие части структуры моего проекта:

cookiecutter_mbam
│   celeryconfig.py   
│
└───cookiecutter_mbam
   |   __init__.py
   │   app.py
   │   run_celery.py
   │
   └───utility
   |       celery_utils.py
   |
   └───derivation 
   |       tasks.py  
   | 
   └───storage
   |       tasks.py    
   |
   └───xnat
          tasks.py

__init__.py:

"""Main application package."""

from celery import Celery

celery = Celery('cookiecutter_mbam', config_source='cookiecutter_mbam.celeryconfig')

Соответствующая часть app.py:

from cookiecutter_mbam import celery

def create_app(config_object='cookiecutter_mbam.settings'):
    """An application factory, as explained here: http://flask.pocoo.org/docs/patterns/appfactories/.

    :param config_object: The configuration object to use.
    """
    app = Flask(__name__.split('.')[0])
    app.config.from_object(config_object)
    init_celery(app, celery=celery)
    register_extensions(app)
    # ...
    return app

run_celery.py:

from cookiecutter_mbam.app import create_app
from cookiecutter_mbam import celery
from cookiecutter_mbam.utility.celery_utils import init_celery

app = create_app(config_object='cookiecutter_mbam.settings')
init_celery(app, celery)

celeryconfig.py:

broker_url = 'redis://localhost:6379'
result_backend = 'redis://localhost:6379'

task_serializer = 'json'
result_serializer = 'json'
accept_content = ['json']
enable_utc = True

imports = {'cookiecutter_mbam.xnat.tasks', 'cookiecutter_mbam.storage.tasks', 'cookiecutter_mbam.derivation.tasks'}

Соответствующая часть celery_utils.py:

def init_celery(app, celery):
    """Add flask app context to celery.Task"""

    class ContextTask(celery.Task):
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return self.run(*args, **kwargs)

    celery.Task = ContextTask
    return celery

Когда я пытаюсь запустить работникаиспользуя celery -A cookiecutter_mbam.run_celery:celery worker я получаю RecursionError: maximum recursion depth exceeded while calling a Python object ошибку.(Я также попробовал несколько других способов вызвать работника, все с той же ошибкой.) Вот выдержка из трассировки стека:

Traceback (most recent call last):
  File "/Users/katie/anaconda/bin/celery", line 11, in <module>
    sys.exit(main())
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/__main__.py", line 16, in main
    _main()
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/celery.py", line 322, in main
    cmd.execute_from_commandline(argv)
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/celery.py", line 496, in execute_from_commandline
    super(CeleryCommand, self).execute_from_commandline(argv)))
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/base.py", line 275, in execute_from_commandline
    return self.handle_argv(self.prog_name, argv[1:])
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/celery.py", line 488, in handle_argv
    return self.execute(command, argv)
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/celery.py", line 420, in execute
    ).run_from_argv(self.prog_name, argv[1:], command=argv[0])
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/worker.py", line 221, in run_from_argv
    *self.parse_options(prog_name, argv, command))
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/base.py", line 398, in parse_options
    self.parser = self.create_parser(prog_name, command)
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/base.py", line 414, in create_parser
    self.add_arguments(parser)
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/bin/worker.py", line 277, in add_arguments
    default=conf.worker_state_db,
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/utils/collections.py", line 126, in __getattr__
    return self[k]
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/utils/collections.py", line 429, in __getitem__
    return getitem(k)
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/utils/collections.py", line 278, in __getitem__
    return mapping[_key]
  File "/Users/katie/anaconda/lib/python3.6/collections/__init__.py", line 989, in __getitem__
    if key in self.data:
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/utils/collections.py", line 126, in __getattr__
    return self[k]
  File "/Users/katie/anaconda/lib/python3.6/collections/__init__.py", line 989, in __getitem__
    if key in self.data:
  File "/Users/katie/anaconda/lib/python3.6/site-packages/celery/utils/collections.py", line 126, in __getattr__
    return self[k]

Я понимаю основной смысл этой ошибки - что-то вызываетсамо по себе бесконечно.Может быть create_app.Но я не понимаю, почему, и я не знаю, как отладить это.

Я также получаю это, когда пытаюсь загрузить свой сайт:

  File "~/cookiecutter_mbam/cookiecutter_mbam/xnat/tasks.py", line 14, in <module>
    @celery.task
AttributeError: module 'cookiecutter_mbam.celery' has no attribute 'task'

У меня не было этой проблемы, когда я использовал make_celery метод, описанный здесь , но этот метод создает проблемы кругового импорта, когда вам нужны задачи для доступа к контексту приложения.Мы будем очень благодарны за советы о том, как сделать это правильно с шаблоном Cookiecutter Flask.

Ответы [ 2 ]

0 голосов
/ 25 февраля 2019

Это решено.У меня был configcelery.py в неправильном месте.Мне нужно было переместить его в каталог пакетов, а не в родительский каталог репо.Это невероятно неинтуитивно / неинформативно, что неуместный файл конфигурации, вместо того, чтобы вызвать ошибку типа «я не могу найти этот файл», вызывает бесконечную рекурсию.Но, по крайней мере, я наконец увидел это и исправил.

0 голосов
/ 24 февраля 2019

Я подозреваю, что этот фрагмент кода делает приложение Flask доступным для сельдерея.Он пропускает некоторый необходимый код, переходя непосредственно к run().(См. https://github.com/celery/celery/blob/master/celery/app/task.py#L387)

Попробуйте вызвать унаследованный __call__. Вот фрагмент из одного из моих (работающих) приложений.

# Arrange for tasks to have access to the Flask app
TaskBase = celery.Task
class ContextTask(TaskBase):
    def __call__(self, *args, **kwargs):
        with app.app_context():
            return TaskBase.__call__(self, *args, **kwargs)  ## << here
celery.Task = ContextTask

Я также не вижу, где выСоздание экземпляра Celery и его настройка. Я предполагаю, что у вас есть

celery = Celery(__name__)

, а затем вам нужно

celery.config_from_object(...)

изнутри init_celery()

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