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

在Pyodide/Stlite中处理API请求:同步等待协程完成方案

在Pyodide中同步等待异步pyfetch完成以初始化类数据

问题核心

你遇到的TypeError: coroutine object is not subscriptable错误,本质是因为:

  1. 标记为async的get_data方法直接调用时,返回的是协程对象而非实际的DataFrame,所以无法用[]下标访问;
  2. 你修改后的get_data里漏了关键一步:response.json()本身也是异步方法,必须用await等待解析完成。

解决方案

以下两种方案可解决同步等待异步请求的问题,根据你的场景选择:


方案一:异步工厂方法(推荐,符合异步编程规范)

将类的初始化逻辑改为异步,通过工厂方法创建实例,避免在__init__中直接处理异步操作:

import asyncio
import pandas as pd
from pyodide.http import pyfetch
from typing import Final

class FabmanData:
    LINKS: Final = {
        "members": "members?orderBy=name&order=asc",
        "resource": "resources?orderBy=name&order=asc",
        "bookings": "bookings?order=desc&limit=50&summary=false",
    }

    def __init__(self) -> None:
        # 先初始化空属性,后续异步填充
        self.members: pd.DataFrame = pd.DataFrame()
        self.resources: pd.DataFrame = pd.DataFrame()
        self.latest_bookings: pd.DataFrame = pd.DataFrame()

    async def init_data(self) -> None:
        # 异步获取并处理所有数据
        self.members = (await self.get_data("members"))[
            ["id", "firstName", "lastName", "memberNumber"]
        ]
        self.resources = (await self.get_data("resource"))[
            ["id", "name", "state"]
        ]
        self.latest_bookings = (await self.get_data("bookings"))[
            ["id", "resource", "fromDateTime", "untilDateTime", "member"]
        ]

    @staticmethod
    async def get_data(category) -> pd.DataFrame:
        url = f"{BASEURL}{FabmanData.LINKS[category]}"
        # 给请求加超时控制(正确用法)
        response = await asyncio.wait_for(
            pyfetch(url=url, headers=HEADERS),
            timeout=10
        )
        # 等待JSON解析完成
        data = await response.json()
        return pd.DataFrame(data)

    @classmethod
    async def create(cls):
        # 异步工厂方法,创建并初始化实例
        instance = cls()
        await instance.init_data()
        return instance

    def get_resources_dict(self):
        return {
            resource: resource_id
            for resource, resource_id in zip(
                self.resources["name"], self.resources["id"]
            )
        }

使用方式:

# 在异步上下文(比如Streamlit的async回调、Pyodide的主异步函数)中创建实例
fabman_data = await FabmanData.create()

方案二:同步上下文用asyncio.run阻塞等待

如果必须在同步的__init__中完成初始化,可以用asyncio.run直接运行协程并阻塞等待结果:

import asyncio
import pandas as pd
from pyodide.http import pyfetch
from typing import Final

class FabmanData:
    LINKS: Final = {
        "members": "members?orderBy=name&order=asc",
        "resource": "resources?orderBy=name&order=asc",
        "bookings": "bookings?order=desc&limit=50&summary=false",
    }

    def __init__(self) -> None:
        # 用asyncio.run阻塞等待协程完成,直接拿到DataFrame
        self.members: pd.DataFrame = asyncio.run(self.get_data("members"))[
            ["id", "firstName", "lastName", "memberNumber"]
        ]
        self.resources: pd.DataFrame = asyncio.run(self.get_data("resource"))[
            ["id", "name", "state"]
        ]
        self.latest_bookings: pd.DataFrame = asyncio.run(self.get_data("bookings"))[
            ["id", "resource", "fromDateTime", "untilDateTime", "member"]
        ]

    @staticmethod
    async def get_data(category) -> pd.DataFrame:
        url = f"{BASEURL}{FabmanData.LINKS[category]}"
        response = await asyncio.wait_for(
            pyfetch(url=url, headers=HEADERS),
            timeout=10
        )
        data = await response.json()
        return pd.DataFrame(data)

    def get_resources_dict(self):
        return {
            resource: resource_id
            for resource, resource_id in zip(
                self.resources["name"], self.resources["id"]
            )
        }

关键注意点

  1. 原代码中await asyncio.wait_for(response, timeout=10)是错误用法,wait_for需要包裹pyfetch调用,才能给整个请求加超时限制;
  2. 确保BASEURL和HEADERS已正确定义(比如HEADERS是包含认证信息的字典,如{"Authorization": "Bearer YOUR_TOKEN"})。

内容的提问来源于stack exchange,提问作者pj43

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 06:02:28