Shiny for Python:会话结束时删除本地文件报错求助
Shiny for Python中session.on_ended触发TypeError的问题排查与解决
我开发的Shiny for Python应用支持用户上传文件并进行服务器端处理,但不希望这些文件留存。参考官方文档编写了如下代码:
from shiny import App, reactive, render, ui import os def server(input, output, session): def delete_uploaded_file(): try: os.remove('file.xlsx'))) except: # If there isn't a file, it's not really a problem pass # This triggers at the end of a session session.on_ended(delete_uploaded_file()) # this closes the session when the user exits out on their browser @reactive.Effect @reactive.event(input.close) async def _(): await session.close() app = App(app_ui, server)
运行时触发了Exception in ASGI application和TypeError,报错回溯信息如下:
ERROR: Exception in ASGI application Traceback (most recent call last): File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\uvicorn\protocols\websockets\websockets_impl.py", line 238, in run_asgi result = await self.app(self.scope, self.asgi_receive, self.asgi_send) File "C:\Users\e365627\AppData\Roaming\Python\Python39\site-packages\uvicorn\middleware\proxy_headers.py", line 78, in __call__ return await self.app(scope, receive, send) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\shiny\_app.py", line 220, in __call__ await self.starlette_app(scope, receive, send) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\starlette\applications.py", line 124, in __call__ await self.middleware_stack(scope, receive, send) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\starlette\middleware\errors.py", line 149, in __call__ await self.app(scope, receive, send) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\shiny\_autoreload.py", line 114, in __call__ return await self.app(scope, receive, send) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\starlette\middleware\exceptions.py", line 79, in __call__ raise exc File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\starlette\middleware\exceptions.py", line 68, in __call__ await self.app(scope, receive, sender) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\starlette\routing.py", line 706, in __call__ await route.handle(scope, receive, send) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\starlette\routing.py", line 341, in handle await self.app(scope, receive, send) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\starlette\routing.py", line 82, in app await func(session) File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\shiny\_app.py", line 294, in _on_connect_cb await session._run() File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\shiny\session\_session.py", line 326, in _run self._run_session_end_tasks() File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\shiny\session\_session.py", line 228, in _run_session_end_tasks self._on_ended_callbacks.invoke() File "C:\Users\username\AppData\Roaming\Python\Python39\site-packages\shiny\_utils.py", line 335, in invoke fn() TypeError: 'NoneType' object is not callable INFO: connection closed
错误原因及解决办法
1. 传递函数时错误调用了函数
session.on_ended(delete_uploaded_file())这行代码的核心问题是:你直接调用了delete_uploaded_file(),导致函数立即执行并返回None,而session.on_ended()需要接收一个可调用的函数对象,而非函数执行后的返回值。当会话结束时,框架尝试调用这个None,就会触发TypeError: 'NoneType' object is not callable。
修正方法:去掉函数后的括号,改为传递函数本身:
session.on_ended(delete_uploaded_file)
2. 语法错误:多余的右括号
os.remove('file.xlsx')))这行代码多了两个右括号,属于语法错误,会导致代码无法正常执行。修正为:
os.remove('file.xlsx')
3. 额外优化建议(可选)
如果是处理用户上传的文件,固定使用file.xlsx存在多用户同时上传时文件被覆盖的风险。建议使用Shiny上传组件返回的临时文件路径,比如假设你的上传组件是ui.input_file("upload", "上传文件"),可以在删除时使用用户上传文件的实际路径:
def delete_uploaded_file(): try: uploaded_file = input.upload() if uploaded_file: os.remove(uploaded_file[0]["datapath"]) except Exception as e: pass
修正后的完整代码示例
from shiny import App, reactive, render, ui import os # 示例UI,包含上传组件和关闭按钮 app_ui = ui.page_fluid( ui.input_file("upload", "上传Excel文件", accept=[".xlsx"]), ui.input_action_button("close", "关闭会话") ) def server(input, output, session): def delete_uploaded_file(): try: # 获取用户上传的文件路径 uploaded_file = input.upload() if uploaded_file: os.remove(uploaded_file[0]["datapath"]) except: pass # 传递函数对象,而非调用函数 session.on_ended(delete_uploaded_file) # 用户点击关闭按钮时关闭会话 @reactive.Effect @reactive.event(input.close) async def _(): await session.close() app = App(app_ui, server)
内容的提问来源于stack exchange,提问作者PipperChip
相关产品推荐
相关产品推荐

