You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 19:30:38