FastAPI 和 Pydantic RecursionError 导致 ASGI 应用程序出现异常

The*_*Fox 6 python pydantic fastapi uvicorn

描述

\n

我见过关于自引用 Pydantic 模型导致的类似问题,RecursionError: maximum recursion depth exceeded in comparison但据我所知,代码中不包含自引用模型。我只是使用 Pydantic 的BaseModel类。

\n

代码成功运行,直到audit.py下面的函数尝试从模型返回输出。

\n

我已经包含了完整的回溯,因为我不确定从哪里开始处理这个错误。我在没有 IDE 的情况下使用 PyCharm 运行了代码,它总是生成下面的回溯,但不会使应用程序崩溃,而是向前端返回 500 的 http 状态代码。

\n

任何建议将不胜感激。

\n

正如所建议的,我还尝试sys.setrecursionlimit(1500)增加递归限制。

\n

环境

\n
    \n
  • 操作系统:Windows 10
  • \n
  • FastAPI版本:0.61.1
  • \n
  • 派丹蒂克版本:1.6.1
  • \n
  • Uvicorn版本:0.11.8
  • \n
  • Python版本:3.7.1
  • \n
  • Pycharm版本:2020.2
  • \n
\n

应用程序

\n

main.py

\n
import uvicorn\nfrom fastapi import FastAPI\nfrom starlette.middleware.cors import CORSMiddleware\n\n\nfrom app.api.routes.router import api_router\nfrom app.core.logging import init_logging\nfrom app.core.config import settings\n\ninit_logging()\n\n\ndef get_app() -> FastAPI:\n    application = FastAPI(title=settings.APP_NAME, version=settings.APP_VERSION, debug=settings.DEBUG)\n\n    if settings.BACKEND_CORS_ORIGINS:\n        # middleware support for cors\n        application.add_middleware(\n            CORSMiddleware,\n            allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],\n            allow_credentials=True,\n            allow_methods=["*"],\n            allow_headers=["*"],\n        )\n    application.include_router(api_router, prefix=settings.API_V1_STR)\n    return application\n\n\napp = get_app()\n\nif __name__ == "__main__":\n    uvicorn.run("main:app", host="127.0.0.1", port=80)\n\n
Run Code Online (Sandbox Code Playgroud)\n

router.py

\n
from fastapi import APIRouter\n\nfrom app.api.routes import audit\n\napi_router = APIRouter()\napi_router.include_router(audit.router, tags=["audit"], prefix="/audit")\n
Run Code Online (Sandbox Code Playgroud)\n

audit.py

\n
import validators\nfrom fastapi import APIRouter, HTTPException\nfrom loguru import logger\n\nfrom app.api.dependencies.audit import analyzer\nfrom app.schemas.audit import AuditPayload, AuditResult\n\nrouter = APIRouter()\n\n\n@router.post("/", response_model=AuditResult, name="audit", status_code=200)\nasync def post_audit(payload: AuditPayload) -> AuditResult:\n    logger.info("Audit request received")\n    # validate URL\n    try:\n        logger.info("Validating URL")\n        validators.url(payload.url)\n    except HTTPException:\n        HTTPException(status_code=404, detail="Invalid URL.")\n        logger.exception("HTTPException - Invalid URL")\n\n    # generate output from route audit.py\n    logger.info("Running audit analysis. This could take up to 10 minutes. Maybe grab a coffee...")\n    analyzed_output = analyzer.analyze(url=payload.url,\n                                       brand=payload.brand,\n                                       twitter_screen_name=payload.twitter_screen_name,\n                                       facebook_page_name=payload.facebook_page_name,\n                                       instagram_screen_name=payload.instagram_screen_name,\n                                       youtube_user_name=payload.youtube_user_name,\n                                       ignore_robots=payload.ignore_robots,\n                                       ignore_sitemap=payload.ignore_sitemap,\n                                       google_analytics_view_id=payload.google_analytics_view_id)\n    output = AuditResult(**analyzed_output)\n    return output \n
Run Code Online (Sandbox Code Playgroud)\n

audit_models.py

\n
from pydantic import BaseModel\n\n\nclass AuditPayload(BaseModel):\n    url: str\n    brand: str\n    twitter_screen_name: str\n    facebook_page_name: str\n    instagram_screen_name: str\n    youtube_user_name: str\n    ignore_robots: bool\n    ignore_sitemap: bool\n    google_analytics_view_id: str\n\n\nclass AuditResult(BaseModel):\n    base_url: str\n    run_time: float\n    website_404: dict\n    website_302: dict\n    website_h1_tags: dict\n    website_duplicate_h1: dict\n    website_h2_tags: dict\n    website_page_duplications: dict\n    website_page_similarities: dict\n    website_page_desc_duplications: dict\n    website_page_title_duplications: dict\n    pages: list\n    pages_out_links_404: dict = None\n    pages_canonicals: dict\n    seo_phrases: dict\n    social: dict\n    google_analytics_report: dict\n    google_psi_desktop: dict\n    google_psi_mobile: dict\n    google_algo_updates: dict\n    google_sb: list\n    robots_txt: list\n\n
Run Code Online (Sandbox Code Playgroud)\n

此行在日志中引发错误:\n2020-09-10 10:02:31.483 | 错误| uvicorn.protocols.http.h11_impl:run_asgi:391 - ASGI 应用程序中的异常

\n

我相信这一点与理解为什么会发生此错误最相关:

\n
  File "pydantic\\main.py", line 623, in pydantic.main.BaseModel._get_value\n  [Previous line repeated 722 more times]\n
Run Code Online (Sandbox Code Playgroud)\n

完整回溯:

\n
Traceback (most recent call last):\n  File "C:\\Users\\<user>\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-P\\ch-0\\202.6948.78\\plugins\\python\\helpers\\pydev\\pydevconsole.py", line 483, in <module>\n    pydevconsole.start_client(host, port)\n    \xe2\x94\x82            \xe2\x94\x82            \xe2\x94\x82     \xe2\x94\x94 50488\n    \xe2\x94\x82            \xe2\x94\x82            \xe2\x94\x94 \'127.0.0.1\'\n    \xe2\x94\x82            \xe2\x94\x94 <function start_client at 0x000001BCEDC19D08>\n    \xe2\x94\x94 <module \'pydevconsole\' from \'C:\\\\Users\\\\<user>\\\\AppData\\\\Local\\\\JetBrains\\\\Toolbox\\\\apps\\\\PyCharm-P\\\\ch-0\\\\202.6948.78\\\\pl...\n  File "C:\\Users\\<user>\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-P\\ch-0\\202.6948.78\\plugins\\python\\helpers\\pydev\\pydevconsole.py", line 411, in start_client\n    process_exec_queue(interpreter)\n    \xe2\x94\x82                  \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console.InterpreterInterface object at 0x000001BCEDC1BF98>\n    \xe2\x94\x94 <function process_exec_queue at 0x000001BCEDC19A60>\n  File "C:\\Users\\<user>\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-P\\ch-0\\202.6948.78\\plugins\\python\\helpers\\pydev\\pydevconsole.py", line 258, in process_exec_queue\n    more = interpreter.add_exec(code_fragment)\n           \xe2\x94\x82           \xe2\x94\x82        \xe2\x94\x94 <_pydev_bundle.pydev_console_types.CodeFragment object at 0x000001BCEDCFE748>\n           \xe2\x94\x82           \xe2\x94\x94 <function BaseCodeExecutor.add_exec at 0x000001BCECF38488>\n           \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console.InterpreterInterface object at 0x000001BCEDC1BF98>\n  File "C:\\Users\\<user>\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-P\\ch-0\\202.6948.78\\plugins\\python\\helpers\\pydev\\_pydev_bundle\\pydev_code_executor.py", line 106, in add_exec\n    more = self.do_add_exec(code_fragment)\n           \xe2\x94\x82    \xe2\x94\x82           \xe2\x94\x94 <_pydev_bundle.pydev_console_types.CodeFragment object at 0x000001BCEDCFE748>\n           \xe2\x94\x82    \xe2\x94\x94 <function InterpreterInterface.do_add_exec at 0x000001BCEDC15D90>\n           \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console.InterpreterInterface object at 0x000001BCEDC1BF98>\n  File "C:\\Users\\<user>\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-P\\ch-0\\202.6948.78\\plugins\\python\\helpers\\pydev\\_pydev_bundle\\pydev_ipython_console.py", line 36, in do_add_exec\n    res = bool(self.interpreter.add_exec(code_fragment.text))\n               \xe2\x94\x82    \xe2\x94\x82           \xe2\x94\x82        \xe2\x94\x82             \xe2\x94\x94 "runfile(\'E:/Users/<user>/Documents/GitHub/HawkSense/backend/app/app/main.py\', wdir=\'E:/Users/<user>/Docume...\n               \xe2\x94\x82    \xe2\x94\x82           \xe2\x94\x82        \xe2\x94\x94 <_pydev_bundle.pydev_console_types.CodeFragment object at 0x000001BCEDCFE748>\n               \xe2\x94\x82    \xe2\x94\x82           \xe2\x94\x94 <function _PyDevFrontEnd.add_exec at 0x000001BCEDC15A60>\n               \xe2\x94\x82    \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console_011._PyDevFrontEnd object at 0x000001BCEDC350B8>\n               \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console.InterpreterInterface object at 0x000001BCEDC1BF98>\n  File "C:\\Users\\<user>\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-P\\ch-0\\202.6948.78\\plugins\\python\\helpers\\pydev\\_pydev_bundle\\pydev_ipython_console_011.py", line 483, in add_exec\n    self.ipython.run_cell(line, store_history=True)\n    \xe2\x94\x82    \xe2\x94\x82       \xe2\x94\x82        \xe2\x94\x94 "runfile(\'E:/Users/<user>/Documents/GitHub/HawkSense/backend/app/app/main.py\', wdir=\'E:/Users/<user>/Docume...\n    \xe2\x94\x82    \xe2\x94\x82       \xe2\x94\x94 <function InteractiveShell.run_cell at 0x000001BCED5E7268>\n    \xe2\x94\x82    \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console_011.PyDevTerminalInteractiveShell object at 0x000001BCEDC350F0>\n    \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console_011._PyDevFrontEnd object at 0x000001BCEDC350B8>\n  File "C:\\Program Files\\Python37\\lib\\site-packages\\IPython\\core\\interactiveshell.py", line 2843, in run_cell\n    raw_cell, store_history, silent, shell_futures)\n    \xe2\x94\x82         \xe2\x94\x82              \xe2\x94\x82       \xe2\x94\x94 True\n    \xe2\x94\x82         \xe2\x94\x82              \xe2\x94\x94 False\n    \xe2\x94\x82         \xe2\x94\x94 True\n    \xe2\x94\x94 "runfile(\'E:/Users/<user>/Documents/GitHub/HawkSense/backend/app/app/main.py\', wdir=\'E:/Users/<user>/Docume...\n  File "C:\\Program Files\\Python37\\lib\\site-packages\\IPython\\core\\interactiveshell.py", line 2869, in _run_cell\n    return runner(coro)\n           \xe2\x94\x82      \xe2\x94\x94 <generator object InteractiveShell.run_cell_async at 0x000001BCEDC49C78>\n           \xe2\x94\x94 <function _pseudo_sync_runner at 0x000001BCED5D0C80>\n  File "C:\\Program Files\\Python37\\lib\\site-packages\\IPython\\core\\async_helpers.py", line 67, in _pseudo_sync_runner\n    coro.send(None)\n    \xe2\x94\x82    \xe2\x94\x94 <method \'send\' of \'generator\' objects>\n    \xe2\x94\x94 <generator object InteractiveShell.run_cell_async at 0x000001BCEDC49C78>\n  File "C:\\Program Files\\Python37\\lib\\site-packages\\IPython\\core\\interactiveshell.py", line 3044, in run_cell_async\n    interactivity=interactivity, compiler=compiler, result=result)\n                  \xe2\x94\x82                       \xe2\x94\x82                \xe2\x94\x94 <ExecutionResult object at 1bcedcd3470, execution_count=2 error_before_exec=None error_in_exec=None info=<ExecutionInfo objec...\n                  \xe2\x94\x82                       \xe2\x94\x94 <IPython.core.compilerop.CachingCompiler object at 0x000001BCEDC356D8>\n                  \xe2\x94\x94 \'last_expr\'\n  File "C:\\Program Files\\Python37\\lib\\site-packages\\IPython\\core\\interactiveshell.py", line 3215, in run_ast_nodes\n    if (yield from self.run_code(code, result)):\n                   \xe2\x94\x82    \xe2\x94\x82        \xe2\x94\x82     \xe2\x94\x94 <ExecutionResult object at 1bcedcd3470, execution_count=2 error_before_exec=None error_in_exec=None info=<ExecutionInfo objec...\n                   \xe2\x94\x82    \xe2\x94\x82        \xe2\x94\x94 <code object <module> at 0x000001BCEDCDADB0, file "<ipython-input-2-086756a0f1dd>", line 1>\n                   \xe2\x94\x82    \xe2\x94\x94 <function InteractiveShell.run_code at 0x000001BCED5E76A8>\n                   \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console_011.PyDevTerminalInteractiveShell object at 0x000001BCEDC350F0>\n  File "C:\\Program Files\\Python37\\lib\\site-packages\\IPython\\core\\interactiveshell.py", line 3291, in run_code\n    exec(code_obj, self.user_global_ns, self.user_ns)\n         \xe2\x94\x82         \xe2\x94\x82    \xe2\x94\x82               \xe2\x94\x82    \xe2\x94\x94 {\'__name__\': \'pydev_umd\', \'__doc__\': \'Automatically created module for IPython interactive environment\', \'__package__\': None,...\n         \xe2\x94\x82         \xe2\x94\x82    \xe2\x94\x82               \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console_011.PyDevTerminalInteractiveShell object at 0x000001BCEDC350F0>\n         \xe2\x94\x82         \xe2\x94\x82    \xe2\x94\x94 <property object at 0x000001BCED5D8958>\n         \xe2\x94\x82         \xe2\x94\x94 <_pydev_bundle.pydev_ipython_console_011.PyDevTerminalInteractiveShell object at 0x000001BCEDC350F0>\n         \xe2\x94\x94 <code object <module> at 0x000001BCEDCDADB0, file "<ipython-input-2-086756a0f1dd>", line 1>\n  File "<ipython-input-2-086756a0f1dd>", line 1, in <module>\n    runfile(\'E:/Users/<user>/Documents/GitHub/HawkSense/backend/app/app/main.py\', wdir=\'E:/Users/<user>/Documents/GitHub/HawkSense/backend/app/app\')\n  File "C:\\Users\\<user>\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-P\\ch-0\\202.6948.78\\plugins\\python\\helpers\\pydev\\_pydev_bundle\\pydev_umd.py", line 197, in runfile\n    pydev_imports.execfile(filename, global_vars, local_vars)  # execute the script\n    \xe2\x94\x82             \xe2\x94\x82        \xe2\x94\x82         \xe2\x94\x82            \xe2\x94\x94 {\'__name__\': \'__main__\', \'__doc__\': "\\nMain entry point into API for endpoints related to HawkSense\'s main functionality.\\nto...\n    \xe2\x94\x82             \xe2\x94\x82        \xe2\x94\x82         \xe2\x94\x94 {\'__name__\': \'__main__\', \'__doc__\': "\\nMain entry point into API for endpoints related to HawkSense\'s main functionality.\\nto...\n    \xe2\x94\x82             \xe2\x94\x82        \xe2\x94\x94 \'E:/Users/<user>/Documents/GitHub/HawkSense/backend/app/app/main.py\'\n    \xe2\x94\x82             \xe2\x94\x94 <function execfile at 0x000001BCECC521E0>\n    \xe2\x94\x94 <module \'_pydev_bundle.pydev_imports\' from \'C:\\\\Users\\\\<user>\\\\AppData\\\\Local\\\\JetBrains\\\\Toolbox\\\\apps\\\\PyCharm-P\\\\ch-0\\\\...\n  File "C:\\Users\\<user>\\AppData\\Local\\JetBrains\\Toolbox\\apps\\PyCharm-P\\ch-0\\202.6948.78\\plugins\\python\\helpers\\pydev\\_pydev_imps\\_pydev_execfile.py", line 18, in execfile\n    exec(compile(contents+"\\n", file, \'exec\'), glob, loc)\n                 \xe2\x94\x82              \xe2\x94\x82              \xe2\x94\x82     \xe2\x94\x94 {\'__name__\': \'__main__\', \'__doc__\': "\\nMain entry point into API for endpoints related to HawkSense\'s main functionality.\\nto...\n                 \xe2\x94\x82              \xe2\x94\x82              \xe2\x94\x94 {\'__name__\': \'__main__\', \'__doc__\': "\\nMain entry point into API for endpoints related to HawkSense\'s main functionality.\\nto...\n                 \xe2\x94\x82              \xe2\x94\x94 \'E:/Users/<user>/Documents/GitHub/HawkSense/backend/app/app/main.py\'\n                 \xe2\x94\x94 \'#!/usr/bin/env python\\n\\n"""\\nMain entry point into API for endpoints related to HawkSense\\\'s main functionality.\\ntodo: htt...\n  File "E:/Users/<user>/Documents/GitHub/HawkSense/backend/app/app\\main.py", line 47, in <module>\n    uvicorn.run("main:app", host="127.0.0.1", port=80)  # for debug only\n    \xe2\x94\x82       \xe2\x94\x94 <function run at 0x000001BCEDE041E0>\n    \xe2\x94\x94 <module \'uvicorn\' from \'C:\\\\Program Files\\\\Python37\\\\lib\\\\site-packages\\\\uvicorn\\\\__init__.py\'>\n  File "C:\\Program Files\\Python37\\lib\\site-packages\\uvicorn\\main.py", line 362, in run\n    server.run()\n    \xe2\x94\x82      \xe2\x94\x94 <function Server.run at 0x000001BCEDE4B510>\n    \xe2\x94\x94 <uvicorn.main.Server object at 0x000001BCFC722198>\n  File "C:\\Program Files\\Python37\\lib\\site-packages\\uvicorn\\main.py", line 390, in run\n    loop.run_until_complete(self.serve(sockets=sockets))\n    \xe2\x94\x82    \xe2\x94\x82                  \xe2\x94\x82    \xe2\x94\x82             \xe2\x94\x94 None\n    \xe2\x94\x82    \xe2\x94\x82                  \xe2\x94\x82    \xe2\x94\x94 <function Server.serve at 0x000001BCEDE4B598>\n    \xe2\x94\x82    \xe2\x94\x82                  \xe2\x94\x94 <uvicorn.main.Server object at 0x000001BCFC722198>\n    \xe2\x94\x82    \xe2\x94\x94 <function BaseEventLoop.run_until_complete at 0x000001BCED49FE18>\n    \xe2\x94\x94 <_WindowsSelectorEventLoop running=True closed=False debug=False>\n  File "C:\\Program Files\\Python37\\lib\\asyncio\\base_events.py", line 560, in run_until_complete\n    self.run_forever()\n    \xe2\x94\x82    \xe2\x94\x94 <function BaseEventLoop.run_forever at 0x000001BCED49FD90>\n    \xe2\x94\x94 <_WindowsSelectorEventLoop running=True closed=False debug=False>\n  File "C:\\Program Files\\Python37\\lib\\asyncio\\base_events.py", line 528, in run_forever\n    self._run_once()\n    \xe2\x94\x82    \xe2\x94\x94 <function BaseEventLoop._run_once at 0x000001BCED4A27B8>\n    \xe2\x94\x94 <_WindowsSelectorEventLoop running=True closed=False debug=False>\n  File "C:\\Program Files\\Python37\\lib\\asyncio\\base_events.py", line 1764, in _run_once\n    handle._run()\n    \xe2\x94\x82      \xe2\x94\x94 <function Handle._run at 0x000001BCED43AB70>\n    \xe2\x94\x94 <Handle <TaskStepMethWrapper object at 0x000001BCFC7D4B00>()>\n  File "C:\\Program Files\\Python37\\lib\\asyncio\\events.py", line 88, in _run\n    self._context.run(self._callback, *self._args)\n    \xe2\x94\x82    \xe2\x94\x82            \xe2\x94\x82    \xe2\x94\x82           \xe2\x94\x82    \xe2\x94\x94 <member \'_args\' of \'Handle\' objects>\n    \xe2\x94\x82    \xe2\x94\x82            \xe2\x94\x82    \xe2\x94\x82           \xe2\x94\x94 <Handle <TaskStepMethWrapper object at 0x000001BCFC7D4B00>()>\n    \xe2\x94\x82    \xe2\x94\x82            \xe2\x94\x82    \xe2\x94\x94 <member \'_callback\' of \'Handle\' objects>\n    \xe2\x94\x82    \xe2\x94\x82            \xe2\x94\x94 <Handle <TaskStepMethWrapper object at 0x000001BCFC7D4B00>()>\n    \xe2\x94\x82    \xe2\x94\x94 <member \'_context\' of \'Handle\' objects>\n    \xe2\x94\x94 <Handle <TaskStepMethWrapper object at 0x000001BCFC7D4B00>()>\n> File "C:\\Program Files\\Python37\\lib\\site-packages\\uvicorn\\protocols\\http\\h11_impl.py", line 388, in run_asgi\n    result = await app(self.scope, self.receive, self.send)\n                   \xe2\x94\x82   \xe2\x94\x82    \xe2\x94\x82      \xe2\x94\x82    \xe2\x94\x82        \xe2\x94\x82    \xe2\x94\x94 <function RequestResponseCycle.send at 0x000001BCFC757840>\n                   \xe2\x94\x82   \xe2\x94\x82    \xe2\x94\x82      \xe2\x94\x82    \xe2\x94\x82        \xe2\x94\x94 <uvicorn.protocols.http.h11_impl.RequestResponseCycle object at 0x000001BCFC7D4A90>\n                   \xe2\x94\x82   \xe2\x94\x82    \xe2\x94\x82      \xe2\x94\x82    \xe2\x94\x94 <function RequestResponseCycle.receive at 0x000001BCFC7578C8>\n                   \xe2\x94\x82   \xe2\x94\x82    \xe2\x94\x82      \xe2\x94\x94 <uvicorn.protocols.http.h11_impl.RequestResponseCycle object at 0x000001BCFC7D4A90>\n                   \xe2\x94\x82   \xe2\x94\x82    \xe2\x94\x94 {\'type\': \'http\', \'asgi\': {\'version\': \'3.0\', \'spec_version\': \'2.1\'}, \'http_version\': \'1.1\', \'server\': (\'127.0.0.1\', 80), \'clie...\n                   \xe2\x94\x82   \xe2\x94\x94 <uvicorn.protocols.http.h11_impl.RequestResponseCycle object at 0x000001BCFC7D4A90>\n                   \xe2\x94\x94 <uvicorn.middleware.proxy_headers.ProxyHeadersMiddleware object at 0x000001BCFC722BA8>\n  File "C:\\Program Files\\Python37\

The*_*Fox 6

这是一个简单的问题,通过修改输出响应以匹配 pydantic 模型即可解决。

这涉及确保其output结构audit.py和数据类型与 pydantic 模型的 AuditResult 类中指定的结构和数据类型相同。

  • 此评论缺乏有关如何解决该问题的基本信息。请详细说明所提出的解决方案 (5认同)