Pyinstaller编译的Uvicorn服务器无法正确启动

问题描述

当我启动server.exe并尝试执行uvicorn.run()时,抛出了异常:

Traceback (most recent call last):
  File "logging\config.py",line 390,in resolve
ModuleNotFoundError: No module named 'uvicorn.logging'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "logging\config.py",line 542,in configure
  File "logging\config.py",line 654,in configure_formatter
  File "logging\config.py",line 469,in configure_custom
  File "logging\config.py",line 397,in resolve
  File "logging\config.py",in resolve
ValueError: Cannot resolve 'uvicorn.logging.DefaultFormatter': No module named 'uvicorn.logging'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "server.py",line 82,in <module>
  File "server.py",line 21,in run
  File "uvicorn\main.py",line 343,in run
  File "uvicorn\config.py",line 180,in __init__
  File "uvicorn\config.py",line 223,in configure_logging
  File "logging\config.py",line 808,in dictConfig
  File "logging\config.py",line 545,in configure
ValueError: Unable to configure formatter 'default'
[7932] Failed to execute script server

请注意,uvicorn.logging模块确实存在,当我用Python执行服务器代码时,它可以正常运行。

解决方法

我遇到了同样的问题。我发现这是hiddenimports的工作,修改xxx.spec中的以下几行很有用:

a = Analysis(['xxx.py'],hiddenimports=['uvicorn.logging'],<everything else>)

但是,仍然会有其他类似的问题。因此,我尝试添加uvicorn的所有文件,并且可以使用:

hiddenimports=['uvicorn.lifespan.off','uvicorn.lifespan.on','uvicorn.lifespan','uvicorn.protocols.websockets.auto','uvicorn.protocols.websockets.wsproto_impl','uvicorn.protocols.websockets_impl','uvicorn.protocols.http.auto','uvicorn.protocols.http.h11_impl','uvicorn.protocols.http.httptools_impl','uvicorn.protocols.websockets','uvicorn.protocols.http','uvicorn.protocols','uvicorn.loops.auto','uvicorn.loops.asyncio','uvicorn.loops.uvloop','uvicorn.loops','uvicorn.logging'],

然后运行:

pyinstaller xxx.spec
,

更通用的方式也是指定钩子:

# extra-hooks/hooks-uvicorn.py
from PyInstaller.utils.hooks import collect_submodules

hiddenimports = collect_submodules('uvicorn')

并使用--additional-hooks-dir extra-hooks运行pyinstaller。例如:

pyinstaller -y --clean --additional-hooks-dir extra-hooks main.py

但是!

如果您像我一样使用websockets lib。您将遇到错误:

File "uvicorn\protocols\websockets\websockets_impl.py",line 24,in <module>
AttributeError: module 'websockets' has no attribute 'WebSocketServerProtocol'
[1844] Failed to execute script main

为此,我找到了另一个解决方案。添加整个uvicorn的目录本身:

# extra-hooks/hooks-uvicorn.py
from PyInstaller.utils.hooks import get_package_paths

datas = [(get_package_paths('uvicorn')[1],'uvicorn')]

并使用上述命令构建exe。