FastAPI开发:如何像Jupyter Notebook一样逐行调试函数并检查变量?
当然有办法!我平时开发FastAPI的时候也经常遇到这种需求——不想每次都启动uvicorn服务器,只想像调试普通Python代码一样,逐行测试函数里的逻辑,熟悉每个变量的含义和接口。下面给你几个实用的方案,一步步解决你的问题:
方案1:把核心业务逻辑抽成独立函数
这是最推荐的做法,既方便调试,也利于后续写单元测试。你可以把upload_dataset_to_server里处理文件、生成DataFrame、保存数据的核心逻辑拆出来,变成一个不依赖FastAPI框架的普通函数:
import pandas as pd from io import StringIO import ml # 假设你的ml模块已经存在 def process_file(raw_content: bytes, filename: str, encoding='utf-8', delimiter=',', sheets='Sheet1'): """独立的文件处理逻辑,和FastAPI框架解耦""" try: if 'xlsx' in filename.split('.')[-1]: df = pd.read_excel(io=raw_content, sheet_name=sheets) filename = filename.replace('xlsx', 'csv') filepath = ml.folder_path + f'\\datastore\\dataset\\{filename}' else: content = str(raw_content, encoding=encoding) data = StringIO(content) df = pd.read_csv(filepath_or_buffer=data, encoding=encoding, delimiter=delimiter) filepath = ml.folder_path + f'\\datastore\\dataset\\{filename}' df.to_csv(path_or_buf=filepath, index_label=False) resp = ml.store_dataframe(df) return resp, df # 返回结果和DataFrame方便调试 except UnicodeDecodeError as error: return str(error), None
然后在Jupyter Notebook里,你就可以随便构造测试数据,逐行或者整块运行这个函数了:
# 测试CSV文件 test_csv_content = b"id,name,age\n1,Alice,25\n2,Bob,30" resp, df = process_file(test_csv_content, "test.csv") print("处理结果:", resp) display(df) # 在Jupyter里直接展示DataFrame # 测试XLSX文件(先准备一个本地的test.xlsx文件) with open("test.xlsx", "rb") as f: test_xlsx_content = f.read() resp, df = process_file(test_xlsx_content, "test.xlsx") print("处理结果:", resp) display(df)
这样你就能随时查看raw_content转成的content、data对象,以及生成的df是什么样的,完全不用启动服务器。
方案2:模拟FastAPI的UploadFile对象
如果你不想改动原函数,也可以直接在Jupyter里模拟FastAPI依赖的UploadFile对象,然后直接调用原函数。UploadFile是Starlette库提供的类,我们可以手动构造它:
from fastapi import UploadFile from io import BytesIO from your_module import upload_dataset_to_server # 导入你的原函数 # 构造模拟的UploadFile test_csv_file = UploadFile( filename="test.csv", file=BytesIO(b"id,name,age\n1,Alice,25\n2,Bob,30") ) # 因为原函数是async的,在Jupyter里可以直接用await执行 result = await upload_dataset_to_server(file=test_csv_file) print("接口返回结果:", result) # 如果你的Jupyter环境不支持直接await,就用asyncio.run包裹 import asyncio result = asyncio.run(upload_dataset_to_server(file=test_csv_file)) print("接口返回结果:", result)
这种方式可以完全模拟真实的请求场景,你甚至可以在原函数里加print语句或者断点,比如在raw = await file.read()后面加print(raw),就能看到读取到的文件内容了。
方案3:用FastAPI的TestClient发起测试请求
FastAPI官方提供了TestClient工具,本质是用requests库模拟HTTP请求,你可以在Jupyter里用它来调用你的端点,同时调试函数内部:
from fastapi.testclient import TestClient from your_module import app # 导入你的FastAPI app实例 client = TestClient(app) # 上传本地的测试文件 with open("test.csv", "rb") as f: response = client.post( "/upload_dataset", files={"file": ("test.csv", f, "text/csv")}, params={"encoding": "utf-8", "delimiter": ","} ) print("响应状态码:", response.status_code) print("响应内容:", response.json())
如果想调试函数内部的细节,你可以在原函数里插入断点,比如:
async def upload_dataset_to_server(file: UploadFile = File(...), encoding='utf-8', delimiter=',', sheets='Sheet1'): raw = await file.read() print("读取到的原始字节:", raw) # 打印原始数据 import pdb; pdb.set_trace() # 插入断点,运行到这里会暂停 # 后续代码...
然后运行TestClient的请求,程序会在断点处暂停,你可以查看raw、file等变量的详细信息,还能逐行执行后续代码。
额外调试技巧
- 在Jupyter里可以用
%debug魔法命令,当代码抛出异常时,输入%debug就能进入调试模式,查看报错时的变量状态。 - 用
display()函数代替print(),可以在Jupyter里更友好地展示DataFrame、字典等复杂对象。
内容的提问来源于stack exchange,提问作者Della

