FastAPI路由调用subprocess执行Python脚本未完成,返回非零状态1
问题分析与解决
核心问题原因
你遇到的报错根本原因是脚本里的fig.show()调用。终端运行时你处于有桌面环境的会话,fig.show()能正常弹出图形窗口,但FastAPI服务运行的环境(尤其是生产环境或无桌面的服务器环境)没有图形显示上下文(DISPLAY环境变量不存在),fig.show()尝试打开GUI窗口失败,直接导致脚本返回非零退出码1——这和GeoJSON加载无关,哪怕你移除后续代码,只要保留这行就会报错。
快速修复步骤
- 删除/注释
fig.show():脚本是后台生成图片用的,完全不需要弹出显示窗口,这行属于冗余代码。 - 统一使用绝对路径:subprocess的工作目录不一定和你终端运行时一致,相对路径极易导致文件找不到问题。可以用
__file__动态计算绝对路径:
FastAPI路由里的import os # 获取脚本所在目录的绝对路径 script_dir = os.path.dirname(os.path.abspath(__file__)) # 拼接图片保存路径 map_save_path = os.path.join(script_dir, '../images/map.png') fig.write_image(map_save_path) # 拼接GeoJSON文件路径 geojson_path = os.path.join(script_dir, '../geojsons/guam.geojson') with open(geojson_path) as f: guam_geojson = json.load(f)script_path和FileResponse的路径也建议换成绝对路径,避免工作目录不一致的坑。 - 检查subprocess参数:你代码里的
subprocess.run传了json参数,但脚本里没处理命令行参数,如果不需要传参就直接删掉,否则要在脚本里用sys.argv接收。
更优实现方案:直接调用函数而非subprocess
用subprocess调用脚本是低效且易出问题的方式,更好的做法是把脚本逻辑封装成函数,在FastAPI中直接导入调用:
改造
get_map.py为模块,封装生成图片的函数:# maps/get_map.py import ssl ssl._create_default_https_context = ssl._create_unverified_context import os import json import plotly.graph_objects as go import pandas as pd def generate_maps(): script_dir = os.path.dirname(os.path.abspath(__file__)) # 生成第一个地图 df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_february_us_airport_traffic.csv') df['text'] = df['airport'] + ' ' + df['city'] + ', ' + df['state'] + ' Arrivals: ' + df['cnt'].astype(str) fig = go.Figure(data=go.Scattergeo( lon=df['long'], lat=df['lat'], text=df['text'], mode='markers', marker_color=df['cnt'], )) fig.update_layout( title='Most trafficked US airports<br>(Hover for airport names)', geo_scope='usa', ) map_path = os.path.join(script_dir, '../images/map.png') fig.write_image(map_path) # 加载GeoJSON并生成关岛地图 geojson_path = os.path.join(script_dir, '../geojsons/guam.geojson') with open(geojson_path) as f: guam_geojson = json.load(f) # 这里补充生成usa_guam地图的逻辑 # ... us_guam_save_path = os.path.join(script_dir, '../images/us_guam_map.png') usa_guam.save(us_guam_save_path) return us_guam_save_path在FastAPI路由中直接调用该函数:
from fastapi import FastAPI from fastapi.responses import FileResponse from maps.get_map import generate_maps app = FastAPI() @app.get('/getMap') def get_map(): try: map_path = generate_maps() return FileResponse(map_path) except Exception as e: return {"message": "Error", "error": str(e)}
这种方式避免了subprocess的上下文切换、环境不一致等问题,代码更易维护,执行效率也更高。
内容的提问来源于stack exchange,提问作者bestfirstsearch
相关产品推荐
相关产品推荐

