Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: Windows下无法进行异步子进程调用 #376

Closed
MIXISAMA opened this issue May 23, 2021 · 9 comments
Closed

Bug: Windows下无法进行异步子进程调用 #376

MIXISAMA opened this issue May 23, 2021 · 9 comments
Labels
bug Something isn't working wontfix This will not be worked on

Comments

@MIXISAMA
Copy link

await asyncio.create_subprocess_shell(cmd)

我平时使用macos和windows环境开发,上面的代码在macos中使用是没有任何问题的,但在windows中会报一个NotImplementedError错误,官方提到

注解 如果使用了 ProactorEventLoop 则子进程将在 Windows 中可用。 详情参见 Windows 上的子进程支持。

结合网上的解决办法,我可以通过修改nonebot/drivers/fastapi.py源码的方式解决这个问题

class Driver(BaseDriver):
    @overrides(BaseDriver)
    def run(self,
            host: Optional[str] = None,
            port: Optional[int] = None,
            *,
            app: Optional[str] = None,
            **kwargs):
        """使用 ``uvicorn`` 启动 FastAPI"""
        super().run(host, port, app, **kwargs)
        LOGGING_CONFIG = {
            "version": 1,
            "disable_existing_loggers": False,
            "handlers": {
                "default": {
                    "class": "nonebot.log.LoguruHandler",
                },
            },
            "loggers": {
                "uvicorn.error": {
                    "handlers": ["default"],
                    "level": "INFO"
                },
                "uvicorn.access": {
                    "handlers": ["default"],
                    "level": "INFO",
                },
            },
        }
        # 以下是修改的内容
        from uvicorn import Config, Server
        from asyncio import ProactorEventLoop, set_event_loop, get_event_loop
        set_event_loop(ProactorEventLoop())
        server = Server(config=Config(
            app or self.server_app,
            host=host or str(self.config.host),
            port=port or self.config.port,
            reload=bool(app) and self.config.debug,
            reload_dirs=self.fastapi_config.fastapi_reload_dirs or None,
            debug=self.config.debug,
            log_config=LOGGING_CONFIG,
            **kwargs,
        ))
        get_event_loop().run_until_complete(server.serve())

        # uvicorn.run(app or self.server_app,
        #             host=host or str(self.config.host),
        #             port=port or self.config.port,
        #             reload=bool(app) and self.config.debug,
        #             reload_dirs=self.fastapi_config.fastapi_reload_dirs or None,
        #             debug=self.config.debug,
        #             log_config=LOGGING_CONFIG,
        #             **kwargs)

但这应该只适用于windows,而且我不知道这样修改是否会带来副作用,希望nb框架能提供更好的解决方案

@MIXISAMA MIXISAMA added the enhancement New feature or request label May 23, 2021
@yanyongyu
Copy link
Member

你可以直接在bot.py中修改eventloop类型而不是修改源码,这样兼容性非常差

@yanyongyu yanyongyu removed the enhancement New feature or request label May 23, 2021
@MIXISAMA
Copy link
Author

你可以直接在bot.py中修改eventloop类型而不是修改源码,这样兼容性非常差

稍后我会尝试,感谢

@SK-415
Copy link
Contributor

SK-415 commented May 23, 2021

我的解决方法是,把下面这段在 nonebot.run() 执行之前导入即可。感谢 @mnixry 佬提供的猴子补丁思路。

import asyncio
import platform
from uvicorn.loops import asyncio as _asyncio
from uvicorn import config
from nonebot.log import logger


def asyncio_setup():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)


@property
def should_reload(self):
    return False


if platform.system() == "Windows":
    _asyncio.asyncio_setup = asyncio_setup
    config.Config.should_reload = should_reload

我是尝试在 NoneBot2 插件中使用 Playwright 时遇到了类似错误:

Task exception was never retrieved
future: <Task finished name='Task-56' coro=<Connection.run() done, defined at c:\users\***\appdata\local\pypoetry\cache\virtualenvs\haruka-bot-b4ae0nxj-py3.9\lib\site-packages\playwright\_impl\_connection.py:163> exception=NotImplementedError()>
Traceback (most recent call last):
  File "c:\users\***\appdata\local\pypoetry\cache\virtualenvs\haruka-bot-b4ae0nxj-py3.9\lib\site-packages\playwright\_impl\_connection.py", line 166, in run
    await self._transport.run()
  File "c:\users\***\appdata\local\pypoetry\cache\virtualenvs\haruka-bot-b4ae0nxj-py3.9\lib\site-packages\playwright\_impl\_transport.py", line 57, in run
    self._proc = proc = await asyncio.create_subprocess_exec(
  File "C:\Users\***\AppData\Local\Programs\Python\Python39\lib\asyncio\subprocess.py", line 236, in create_subprocess_exec
    transport, protocol = await loop.subprocess_exec(
  File "C:\Users\***\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 1661, in subprocess_exec
    transport = await self._make_subprocess_transport(
  File "C:\Users\***\AppData\Local\Programs\Python\Python39\lib\asyncio\base_events.py", line 493, in _make_subprocess_transport
    raise NotImplementedError
NotImplementedError

查阅 资料 后发现,是因为 Uvicorn 在 Windows 平台下使用的并非默认的 ProactorEventLoop,而是 SelectorEventLoop。原因是 ProactorEventLoop 无法实现 reload。

所以解决办法就是使用猴子补丁把 Windows 下使用的 EventLoop 再改回默认的。但同时 Uvicorn 的 reload 也将无法使用,不过因此推测,没有其他更多影响,生产环境可以正常使用。

PS:写到一半看到 yyy 佬提到 可以直接在bot.py中修改eventloop类型,如果可以的话这会比我猴子补丁的方法兼容性更好,我晚点会尝试是否可行。

@MIXISAMA
Copy link
Author

from asyncio import ProactorEventLoop, set_event_loop
set_event_loop(ProactorEventLoop())

我尝试在bot.py中加入以上两行,并不起作用。希望您能直接提供“直接在bot.py中修改eventloop类型”的源码示例。我认为SK-415的方案足够好了,如果不能提供示例,可以关闭这个ISSUE,我会使用SK-415的解决方案。

@yanyongyu
Copy link
Member

请问你是在哪里调用的

await asyncio.create_subprocess_shell(cmd)

@MIXISAMA
Copy link
Author

请问你是在哪里调用的

await asyncio.create_subprocess_shell(cmd)

https://github.com/MIXISAMA/zaobot/blob/1698ccecbbe1038f8121ece1f518be4800f11325/src/plugins/phlogo/view.py#L30

@yanyongyu
Copy link
Member

查看了一下 uvicorn 的源码,写死的

https://github.com/encode/uvicorn/blob/bf1c64e2c141971c546671c7dc91b8ccf0afeb7d/uvicorn/loops/asyncio.py#L7-L18

看上去只能做monkey patch

@mnixry mnixry changed the title Feature: 在windows10中实现子进程 Bug: Windows下无法进行异步子进程调用 May 23, 2021
@mnixry mnixry added the bug Something isn't working label May 23, 2021
@yanyongyu yanyongyu added the wontfix This will not be worked on label May 24, 2021
@yanyongyu
Copy link
Member

如果没有问题的话先close了

@mnixry
Copy link
Member

mnixry commented Aug 20, 2021

该问题的解决方案已被添加至FAQ以供用户参考:nonebot/discussions#13 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working wontfix This will not be worked on
Development

No branches or pull requests

4 participants