Gunicorn: Проблема v20: не удалось найти объект приложения 'create_app ()' в 'app'

Созданный на 9 нояб. 2019  ·  47Комментарии  ·  Источник: benoitc/gunicorn

Я забыл закрепить свою версию Gunicorn, и команда run начала ломаться сегодня утром, когда я повторно развернул свое приложение, и оно автоматически обновилось до 20.0.

Понижение моей версии Gunicorn до 19.9 устранило проблему.

Это команда, которую я использую для запуска своего приложения:

gunicorn 'app:create_app()' --workers 4 --threads 4 --bind 0.0.0.0:$PORT

Ошибка:

Failed to find application object 'create_app()' in 'app'
( Feedback Requested FeaturApp Investigation

Самый полезный комментарий

исправлено в мастере. спасибо @davidism за патч!

все обрабатываемые случаи находятся в этих тестах: https://github.com/benoitc/gunicorn/commit/19cb68f4c3b55da22581c008659ee62d8c54ab2b#diff -5832adf374920d75d4bf48e546367f53R67

Все 47 Комментарий

Я тоже столкнулся с этой проблемой, т.е.
Failed to find application object 'create_app()' in 'app'
и закрепление до версии 19.9.0 решает проблему.

Первоначально я думал, что исправление заключалось в изменении команды gunicorn с:
gunicorn --bind 0.0.0.0:$PORT app:create_app()
к:
gunicorn --bind 0.0.0.0:$PORT app:create_app
(обратите внимание, что скобки после create_app теперь удалены). Поначалу вроде все хорошо:

website_1 | [2019-11-10 19:18:54 +0000] [1] [ИНФОРМАЦИЯ] Запуск Gunicorn 20.0.0
website_1 | [2019-11-10 19:18:54 +0000] [1] [INFO] Слушает: http://0.0.0.0 : 8000 (1)
website_1 | [2019-11-10 19:18:54 +0000] [1] [INFO] Использование worker: sync
website_1 | [2019-11-10 19:18:54 +0000] [11] [INFO] Загрузочный воркер с pid: 11

Но, увы, это всего лишь мираж, потому что, когда вы попытаетесь загрузить свой веб-сайт / конечную точку флешки, он скажет:

[2019-11-10 19:20:28 +0000] [11] [ERROR] Ошибка обработки запроса /
website_1 | Отслеживание (последний вызов последний):
website_1 | Файл "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", строка 134, в дескрипторе
website_1 | self.handle_request (слушатель, запрос, клиент, адрес)
website_1 | Файл "/usr/local/lib/python3.7/site-packages/gunicorn/workers/sync.py", строка 175, в handle_request
website_1 | respiter = self.wsgi (среда, соответственно start_response)
website_1 | TypeError: create_app () принимает 0 позиционных аргументов, но дано 2

Это явно проблема с Gunicorn версии 20.0.0.

Это должно быть связано с этим изменением: https://github.com/benoitc/gunicorn/commit/3701ad9f26a7a4c0a081dfd0f6e97ecb272de515#diff -0b90f794c3e9742c45bf484505e3db8dR377 через # 2043.

Один из способов исправить это на вашей стороне - экспортировать myapp = create_app() в ваш основной модуль и десять начать с app:myapp . Это должно сработать, дайте мне знать, если это не так.

Я посмотрю, нужно ли там что-то делать. @berkerpeksag, зачем там eval ?

Это должно быть связано с этим изменением: 3701ad9 # diff-0b90f794c3e9742c45bf484505e3db8dR377 через # 2043.

Один из способов исправить это на вашей стороне - экспортировать myapp = create_app() в ваш основной модуль и десять начать с app:myapp . Это должно сработать, дайте мне знать, если это не так.

Я посмотрю, нужно ли там что-то делать. @berkerpeksag, зачем там eval ?

Я внес это изменение в свое приложение и исправил сбой, теперь Gunicorn может запускать мое приложение, сохраняя результат create_app() в переменной и экспортируя его, чтобы его можно было использовать в моей команде запуска Gunicorn

# app.py
def create_app():
    ...

my_app = create_app()

gunicorn "app:my_app" --workers 8

Я могу подтвердить, что выполнение того, что предложили @benoitc и @ jrusso1020, устраняет проблему. Спасибо всем!

Я не вижу, чтобы исправление работало, если во время запуска требуется передача параметров, например:

gunicorn --chdir hcli_core path "hcli_ core: HCLI (" hcli_core sample hfm ") .connector".

Передача параметров работает в 19.9.0, но не работает в 20.0.0.

@benoitc на случай, если вам будет полезно знать, в документации по фляжке рекомендуется использовать шаблон app:create_app() при использовании gunicorn. Я подозреваю, что некоторые из ваших новых пользователей сначала попробуют gunicorn в результате создания флеш-приложений, и они попытаются использовать ныне нарушенную рекомендацию из этих документов (по крайней мере, это был мой опыт).

Я могу обратиться к этой команде, чтобы попросить их обновить, но я подожду, пока @berkerpeksag внесет свой вклад в падение exec на случай, если есть смысл вернуть его.

@ tjwaterman99 ну, я не уверен, что мне нравится передавать аргументы в приложение таким образом. Не думаю, что это хорошая идея. Аргументы следует передавать через env.

Наш собственный пример использования Flask делает то, что я описываю. Я думаю, что нынешний способ проще в использовании. Мысли?

cc @tilgovi @berkerpeksag ^^

FWIW, мы тоже сталкиваемся с этим.

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

Я не думаю, что мы когда-либо намеренно поддерживали такие обычаи, как app:callable() и app:callable(some, args) . Я бы сказал, что это был неудачный побочный эффект использования eval() в предыдущей реализации.

Текущая реализация очень близка к тому, что делает import_string() Django:

https://github.com/django/django/blob/master/django/utils/module_loading.py#L7 -L24

Я был бы счастлив улучшить документацию, добавить примечание к выпуску и опубликовать более подробное сообщение об ошибке.

Я не думаю, что мы когда-либо намеренно поддерживали такие способы использования, как app: callable () и app: callable (some, args). Я бы сказал, что это был неудачный побочный эффект использования eval () в предыдущей реализации.

Да, я согласен. Насколько я понимаю, мы никогда не поддерживали такой способ запуска приложения.

Я +1 за более явную ошибку. Может, стоит выдать ошибку, если имя приложения не простое?

Имейте в виду, что это явное поведение, упомянутое в общедоступной документации для одной из основных фреймворков wsgi (flask), ранее поддерживалось вашим проектом. Удаление eval предотвращает ленивый запуск приложения, что является проблемой, если приложение 1) предоставляется библиотекой и 2) требует нетривиальных затрат на установку. Если нет безопасности или другой причины, по которой eval неуместен, не будет ли проще просто продолжать поддерживать ваше существующее поведение?

В случае, если кто-то столкнется с подобным случаем, подходящим обходным путем начиная с Python 3.7 будет подделка переменной уровня модуля путем создания __getattr__ уровня модуля в соответствии с этим PEP . Это позволило бы ленивый запуск (как фабрики приложений), не затрагивая критические изменения в Gunicorn 20.0.0.

Что ж, мы никогда не поддерживали такое поведение, ни в одной из наших документов или примеров оно не используется. Это не подходит для командной строки.

Но верно, это действительно переломное и неожиданное изменение. Тогда я был бы за то, чтобы вернуть eval и предупредить пользователя об устаревшем поведении. Возможно также, чтобы заменить его и позволить людям использовать "фабричный" шаблон проектирования, мы могли бы добавить настройку --init-args :

gunicorn -b :8000 --init-args="arg1,arg2"  app:factory_method

Или что-то в этом роде. Мысли?

@benoitc Было бы отлично поддерживать фабричные методы с явным флагом командной строки 😄 Может быть, что-то вроде:

$ gunicorn -b :8000 \
  --callable \
  --callable-arg "abc" \
  --callable-arg "xyz" \
  --callable-kwarg "key" "value" \
  app:factory_method

(Или, может быть, другое базовое имя, например --factory )

У меня возникли проблемы с этим изменением, потому что у меня больше нет возможности легко запускать тесты. Поскольку (i) мое приложение зависит от переменных среды, (ii) тестовая коллекция загружает все модули (для doctests) и (iii) я больше не могу откладывать создание приложения до момента импорта, я не могу протестировать свой проект без добавления длинной строки переменных среды перед каждой тестовой командой, и тестирование занимает больше времени, чем раньше.

Поскольку я использую Python 3.7, я думаю, что могу обойти это с помощью __getattr__ уровня модуля, но для версий до 3.7 я не думаю, что есть какое-либо решение этой проблемы, кроме перехода на более раннюю версию.

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

@ tjwaterman99 ну, я не уверен, что мне нравится передавать аргументы в приложение таким образом. Не думаю, что это хорошая идея. Аргументы следует передавать через env.

Наш собственный пример использования Flask делает то, что я описываю. Я думаю, что нынешний способ проще в использовании. Мысли?

Я согласен, я думаю, что передача аргументов через среду более интуитивно понятна и побуждает пользователей хранить свою конфигурацию в одном месте. Однако поддержка вызываемых объектов / фабрик важна по крайней мере для Flask и, возможно, для других фреймворков.

+1 за предупреждение и инструкции по использованию Gunicorn на фабриках перед прекращением поддержки exec следующем выпуске.

Очень жаль, что это произошло. У нас есть два варианта ответа. Мы могли бы снова изменить поведение или помочь всем мигрировать.

Если бы мы снова изменили поведение, возможно, имело бы смысл вытащить релиз из PyPI, но я думаю, что это слишком радикально. Gunicorn никогда не документировал и не предлагал такое использование.

Поэтому я предлагаю просто помочь всем адаптироваться и извиниться за неудобства.

Мы должны обратиться к Flask с PR, чтобы обновить их документацию. Я счастлив сделать это. Я думаю, что другие уже документируют здесь путь миграции.

Я добавлю к предложениям, что может быть полезно иметь _separate_ модуль или скрипт, который импортирует фабрику приложений, вызывает ее и экспортирует. Это может служить точкой входа Gunicorn, и его можно исключить из doctest и других инструментов, чтобы он не запускал нежелательный импорт при запуске этих инструментов в разработке. Для этого может сработать что-то вроде сценария __main__.py или web.py .

В будущем мы должны делать релиз-кандидаты доступными, даже если мы думаем, что релизы должны быть безопасными. Мы могли бы уловить это с помощью кандидата на выпуск, а затем иметь возможность задокументировать критическое изменение в наших примечаниях к выпуску или отказаться от него на целый цикл.

Я не думаю, что имеет смысл добавлять поддержку аргументов инициализации в командной строке. Слишком поздно для этого выпуска; мы уже поддерживаем пользовательские приложения для расширенных вариантов использования; и у многих фреймворков есть свои рекомендуемые способы передачи настроек приложениям. Gunicorn не должен предоставлять свои собственные. Попытка добавить аргументы для исправления этой проблемы расширяет поверхность для такого рода критических изменений в будущем. Мы должны стремиться минимизировать поверхность интерфейса командной строки Gunicorn, насколько это возможно.

Мы должны обратиться к Flask с PR, чтобы обновить их документацию. Я счастлив сделать это. Я думаю, что другие уже документируют здесь путь миграции.

Я вижу, что @ bilalshaikh42 уже сделал это на https://github.com/pallets/flask/pull/3421

(Здесь один из сопровождающих Flask)

Хотя я полностью согласен с тем, чтобы избавиться от eval , я думаю, что должна быть явная поддержка фабрик приложений! Весь смысл фабрики приложений состоит в том, чтобы избежать наличия импортируемого объекта app (поскольку его использование часто приводит к аду циклических зависимостей).

В flask run cli (только для разработки) мы фактически добавили явную поддержку фабрик приложений, потому что они очень распространены.

Конечно, создать wsgi.py содержащий from myapp. import make_app; app = make_app() очень просто. Но мне либо нужно поддерживать этот файл отдельно (что неудобно, потому что теперь pip install myapp не устанавливает все необходимое для его запуска), либо помещаю его в свой пакет (что означает, что теперь вы можете импортировать его изнутри. само приложение, что было бы неправильно)

В Flask мы использовали явный способ проверки вызываемой фабрики приложений и ее вызова, не прибегая к eval - может быть, вы могли бы рассмотреть что-то подобное? Если вам нужно меньше магии, вы даже можете использовать разные аргументы CLI для указания на приложение и для указания на фабрику приложений.

В будущем мы должны делать релиз-кандидаты доступными, даже если мы думаем, что релизы должны быть безопасными. Мы могли бы уловить это с помощью кандидата на выпуск, а затем иметь возможность задокументировать критическое изменение в наших примечаниях к выпуску или отказаться от него на целый цикл.

Не уверен, что RC действительно помогают - обычно люди не устанавливают / обновляют с помощью --pre (также из-за того, насколько плохо это работает - это влияет не только на явно указанные пакеты, но и на все вложенные зависимости, независимо от того, насколько глубоко, поэтому очень легко, что некоторая зависимость от зависимости влечет за собой сломанный пререлиз), поэтому любой, кто просто не закрепил свои версии, не заметит никаких поломок, пока не выпустит их.

Как бы то ни было, zope.hookable предоставляет простой способ реализовать ленивый подход, подобный фабрике, практически без накладных расходов (из-за необязательного расширения C). Однако он ничего не делает с передачей дополнительных аргументов.

# app.py
from zope.hookable import hookable

def make_app():
    def _(environ, start_response, value=b'Hello'):
        start_response('200 OK',
                       [('Content-Type', 'text/plain')])
        return [value]
    return _

<strong i="8">@hookable</strong>
def app(environ, start_response):
    real_app = make_app()
    app.sethook(real_app)
    return real_app(environ, start_response, b"First time")
$ gunicorn app:app
[2019-11-12 05:53:47 -0600] [12457] [INFO] Starting gunicorn 20.0.0
[2019-11-12 05:53:47 -0600] [12457] [INFO] Listening at: http://127.0.0.1:8000 (12457)
[2019-11-12 05:53:47 -0600] [12457] [INFO] Using worker: sync
[2019-11-12 05:53:47 -0600] [12460] [INFO] Booting worker with pid: 12460
...
% http localhost:8000
HTTP/1.1 200 OK
Connection: close
Content-Type: text/plain
Date: Tue, 12 Nov 2019 11:53:49 GMT
Server: gunicorn/20.0.0
Transfer-Encoding: chunked

First time

% http localhost:8000
HTTP/1.1 200 OK
Connection: close
Content-Type: text/plain
Date: Tue, 12 Nov 2019 11:53:51 GMT
Server: gunicorn/20.0.0
Transfer-Encoding: chunked

Hello

Конечно, создать wsgi.py содержащий from myapp. import make_app; app = make_app() очень просто. Но мне либо нужно поддерживать этот файл отдельно (что неудобно, потому что теперь pip install myapp не устанавливает все необходимое для его запуска), либо помещаю его в свой пакет (что означает, что теперь вы можете импортировать его изнутри. само приложение, что было бы неправильно)

Другая причина, по которой wsgi.py в вашем проекте неверна: некоторые инструменты импортируют все модули в проекте; например. pytest делает это при поиске документов.

Здесь еще один сопровождающий Flask. @ThiefMaster уже сказал все, что я хотел сказать, поэтому я в основном подтверждаю свою поддержку этой функции.

Я согласен избавиться от eval , и я избегал этого в flask run . Вы можете добавить более ограниченную версию предыдущего поведения. Если параметр командной строки имеет скобки, предположим, что это фабрика, возвращающая реальное приложение. Используйте literal_eval для анализа содержимого парных скобок, затем вызовите фабрику с проанализированными параметрами.

Я думаю, что фабричный паттерн без файла wsgi.py очень ценен. Я хотел бы помочь найти способ сохранить его в Gunicorn.

Кто-нибудь хотел бы составить PR для literal_eval строк приложения, подобных фабрике? Это будет в gunicorn.util.import_app .

Нужно добавить тесты, но вот код Flask, перенесенный на Gunicorn: https://github.com/benoitc/gunicorn/compare/master...davidism : import-factory

@davidism Если вам интересно, вот функция, которая может быть полезна для загрузки приложений с фабрик приложений (с doctests 😄). Он использует встроенный в Python анализатор AST, чтобы различать имена атрибутов и вызовы функций (а не регулярное выражение). Он также поддерживает аргументы ключевого слова в фабричной функции. Все по-прежнему оценивается с использованием ast.parse и ast.literal_eval , поэтому вызовов eval :

import ast
from types import ModuleType
from typing import Any


def get_app(module: ModuleType, obj: str) -> Any:
    """
    Get the app referenced by ``obj`` from the given ``module``.

    Supports either direct named references or app factories, using `ast.literal_eval` for safety.

    Example usage::

        >>> import collections
        >>> get_app(collections, 'Counter')
        <class 'collections.Counter'>
        >>> get_app(collections, 'Counter()')
        Counter()
        >>> get_app(collections, 'import evil_module')  # doctest: +ELLIPSIS
        Traceback (most recent call last):
          ...
        ValueError: Could not parse 'import evil_module' as a reference to a module attribute or app factory.
        >>> get_app(collections, '(lambda: sys.do_evil)()')
        Traceback (most recent call last):
            ...
        ValueError: App factories must be referenced by a simple function name
        >>> get_app(collections, '(1, 2, 3)')
        Traceback (most recent call last):
            ...
        ValueError: Could not parse '(1, 2, 3)' as a reference to a module attribute or app factory.
    """
    # Parse `obj` to an AST expression, handling syntax errors with an informative error
    try:
        # Note that mode='eval' only means that a single expression should be parsed
        # It does not mean that `ast.parse` actually evaluates `obj`
        expression = ast.parse(obj, mode='eval').body
    except SyntaxError as syntax_error:
        raise ValueError("Could not parse '{}' as a reference to a module attribute or app factory.".format(obj)) from syntax_error

    # Handle expressions that just reference a module attribute by name
    if isinstance(expression, ast.Name):
        # Expression is just a name, attempt to get the attribute from the module
        return getattr(module, expression.id)

    # Handle expressions that make a function call (factory)
    if isinstance(expression, ast.Call):
        # Make sure the function name is just a name reference
        if not isinstance(expression.func, ast.Name):
            raise ValueError("App factories must be referenced by a simple function name")

        # Extract the function name, args and kwargs from the call
        try:
            name = expression.func.id
            args = [ast.literal_eval(arg) for arg in expression.args]
            kwargs = {keyword.arg: ast.literal_eval(keyword.value) for keyword in expression.keywords}
        except ValueError as value_error:
            raise ValueError("Could not evaluate factory arguments, please ensure that arguments include only literals.") from value_error

        # Get and call the function, passing in the given arguments:
        return getattr(module, name)(*args, **kwargs)

    # Raise an error, we only support named references and factory methods
    raise ValueError("Could not parse '{}' as a reference to a module attribute or app factory.".format(obj))

Если принято решение поддерживать только фабрики приложений без аргументов, весь код обработки аргументов может быть удален. Он по-прежнему будет хорошо работать, чтобы безопасно различать имена и вызовы фабрики (и будет полезен для предоставления пользователям конкретных сообщений об ошибках, когда они пытаются передать аргументы фабрике)

@ThiefMaster Я все еще не уверен, что мы должны поддерживать такой шаблон. Чем это полезно? Почему бы не использовать переменные среды для передачи настраиваемых аргументов или конфигурации, если это действительно необходимо?

Конечно, создание файла wsgi.py из myapp. import make_app; app = make_app () очень просто. Но мне либо нужно поддерживать этот файл отдельно (что неудобно, потому что теперь pip install myapp не будет устанавливать все необходимое для его запуска), либо поместить его в свой пакет (что означает, что теперь вы можете импортировать его из самого приложения. что было бы неправильно)

Я не понимаю, почему такой файл нужно хранить отдельно?

Если он в пакете, значит, его можно импортировать. Так что, если у вас есть более крупный проект, кто-то в конечном итоге просто импортирует его вместо того, чтобы использовать current_app т. Д., То есть больше работы при работе с PR, содержащими такого рода ошибки.

Если он находится вне пакета, вы не получите его при выполнении pip install .


FWIW, меня не волнуют аргументы. Обычно они не нужны (env vars действительно подходят). Но, по крайней мере, возможность указать на вызываемую фабрику приложений вместо объекта приложения невероятно полезна!

Почему бы не использовать переменные среды для передачи настраиваемых аргументов или конфигурации, если это действительно необходимо?

pytest загружает каждый модуль в проекте для поиска тестов. Если у вас есть глобальный объект app=Flask() который зависит от переменных среды или файла конфигурации, этот объект будет загружен при запуске тестов. Полезно иметь возможность запускать тесты без установки переменных среды или файлов конфигурации. Шаблон фабрики приложений отлично подходит для этого.

Фабрика с шаблоном аргументов довольно распространена из-за некоторых популярных руководств по Flask, поэтому я поддержал ее в flask run . Я согласен с тем, что для настройки приложения предпочтительнее использовать среду, поэтому мне подойдет более сокращенная версия, которая поддерживает вызов фабрики без аргументов.

# an app is a name only
$ gunicorn module:app

# a factory has (), but no args allowed
$ gunicorn module:factory()

@tilgovi Согласен. Моя основная проблема заключается в том, что мы не ожидали, что это кого-то сломает, поэтому я предлагал вернуть eval (или что-то более безопасное) и отказаться от него. С другой стороны, да, такое поведение никогда не поддерживалось и было нежелательным результатом использования eval : /

@davidism интересно. Но чем это будет отличаться от использования вызываемого объекта в качестве приложения?

Я не уверен, что вы имеете в виду, не могли бы вы привести более конкретный пример? Фабрика возвращает приложение WSGI, а не приложение WSGI.

@davidism Я имею в виду кое-что вроде этого


def make_app():
  from mymodule.application import MainApp
  return MainApp()

application = make_app()

затем кто-то запускает gunicorn -b :8000 somemodule:application

Это приводит к тому, что application всегда оценивается при импорте кода, что противоречит цели фабрики.

Вызываемый объект WSGI также может быть экземпляром класса, поэтому, возможно, это было то, что было задумано:

class Application:
    _app = None
    def __call__(self, environ, start_response):
        if self._app is None:
            from wherever import make_app
            self._app = make_app()
        return self._app(environ, start_response)

application = Application()

(Пример zope.hookable по сути тот же, только меньше накладных расходов в установившемся состоянии.)

Иметь приложение WSGI, которое создает настоящее приложение WSGI, не идеально. Теперь это дополнительный вызов функции при каждом запросе прокси к реальной точке входа. Настройка должна быть выполнена до первого запроса, но теперь она откладывается до тех пор, пока первый запрос занимает (потенциально намного) дольше.

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

На этом этапе все, что мы просим, ​​это «если строка импорта заканчивается скобками, вызовите импортированное имя, чтобы получить приложение».

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

Очень ограниченный шаблон «если объект приложения вызывается с нулевыми аргументами, затем вызывать его как фабрику» может работать, но он не работает, если вызываемый объект на самом деле является приложением WSGI, которое плохо оформлено и не раскрывает свои аргументы так легко при самоанализе. . Если мы хотим быть щедрыми, мы должны поддерживать все, что у нас было раньше, избегая при этом eval , так что я думаю, что мы должны поступить именно так.

Я очень ценю все предложения и помогу решить эту проблему, всем.

Мне нравятся оба предложения от @davidism и @connorbrinton с использованием literal_eval .

Это приводит к тому, что приложение всегда оценивается при импорте кода, что противоречит цели фабрики.

Ну, он запустит приложение во время выполнения и вернет вызываемый объект, используемый рабочими. Это не так уж и сложно.

Мой главный задел в отношении этого шаблона - он побуждает людей запускать некоторый предварительный код, который может нарушить ожидания от HUP или USR2. Также это нарушает текущий пользовательский интерфейс. Будет ли он работать с будущим использованием Gunicorn?

В любом случае варианты следующие:

  1. мы можем считать, что это поведение было неподдерживаемым, недокументированным (в Gunicorn). Изменение, которое было сделано на его основе.
  2. некоторые пользователи полагались на это, и теперь мы хотим поддержать это поведение

(1) - трудный, но логичный путь, учитывая, что мы никогда его не поддерживали.
(2) какая-то эстетика и ломает пользовательский интерфейс командной строки, ему нужны некоторые тесты / примеры для тестирования в Gunicorn, используйте что-то вроде literal_evals

Мы можем поддержать (2), но я хотел бы провести небольшое тестирование. Также мы должны это задокументировать?

@tilgovi @jamadden @berkerpeksag @sirkonst, что вы предпочитаете?

Похоже, еще одним критическим вариантом использования был доступ к атрибутам , который не будет решен с помощью literal_eval .

Например, в Plotly тире использовать Dash объект, который внутри имеет Flask экземпляр как server атрибута. Некоторые люди использовали:

gunicorn "module:app.server"

Я не уверен, что это нужно поддерживать. flask run тоже не поддерживает. Похоже, что у объекта Dash должен быть метод __call__ который передается в Flask.__call__ . Кроме того, в документации Dash сказано сделать server = app.server и указать на это Gunicorn, так что, похоже, это в основном случай передачи некорректной информации.

@davidism Я заболел сегодня, но я посмотрю об этом в воскресенье, чтобы выпустить в понедельник. Предложение от @tilgovi хорошее, и общий план состоит в том, чтобы иметь безопасный eval, заменяющий старый eval.

Я думаю, мы должны предупредить пользователя, что он использует такую ​​инициализацию. Мысли ? cc @tilgovi

Я попытаюсь превратить ветку, которую я привел выше, в PR с тестами в субботу, если вы не хотите создать другую реализацию.

@davidism продолжай. Я вернусь в воскресенье и пересмотрю его, если понадобится :) Спасибо!

Немного отстает, работаю над этим сейчас.

@connorbrinton классная идея использовать ast.parse , я попробую и включу вас в качестве соавтора в коммит, если я согласен.

Просто хотел сказать, что есть несколько популярный (и довольно старый) ответ на переполнение стека, который направляет пользователей к поведению v19, которое может нуждаться в обновлении в зависимости от того, какой выбор сделан: https://stackoverflow.com/questions/ 8495367 / использование дополнительных аргументов командной строки с пушкой

исправлено в мастере. спасибо @davidism за патч!

все обрабатываемые случаи находятся в этих тестах: https://github.com/benoitc/gunicorn/commit/19cb68f4c3b55da22581c008659ee62d8c54ab2b#diff -5832adf374920d75d4bf48e546367f53R67

Была ли эта страница полезной?
0 / 5 - 0 рейтинги