feat(server): 平台默认组合——Windows granian+winloop,Unix granian+rloop

- granian[reload,rloop,uvloop,winloop] 改为默认依赖,uv sync 即具备完整启动栈
- auto 在启动器内解析为具体循环:granian Windows→winloop / Unix→rloop(缺省回退 uvloop/asyncio);
  uvicorn Unix→uvloop / Windows→asyncio
- 启动日志打印实际 server+loop;测试覆盖平台默认与回退;README 默认矩阵更新
This commit is contained in:
snowy-yang committed 2026-10-23 22:23:30 +08:00
1 parent 0cae770ace
commit bcfc1022eb
5 files changed
+115 -52

No files matched your search

+48 -2
View File
@@ -45,11 +45,57 @@ def test_uvicorn_rejects_granian_only_loops() -> None:
serve.resolve_loop("uvicorn", "rloop")
def test_uvicorn_auto_and_asyncio_allowed() -> None:
assert serve.resolve_loop("uvicorn", "auto") == "auto"
def test_uvicorn_auto_and_asyncio_allowed(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(serve, "IS_WINDOWS", True)
assert serve.resolve_loop("uvicorn", "auto") == "asyncio"
assert serve.resolve_loop("uvicorn", "asyncio") == "asyncio"
def test_default_loop_granian_windows(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(serve, "IS_WINDOWS", True)
monkeypatch.setattr(serve, "has_module", lambda name: name == "winloop")
assert serve.default_loop("granian") == "winloop"
def test_default_loop_granian_unix_prefers_rloop(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(serve, "IS_WINDOWS", False)
monkeypatch.setattr(serve, "has_module", lambda name: name in ("rloop", "uvloop"))
assert serve.default_loop("granian") == "rloop"
def test_default_loop_granian_unix_falls_back(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(serve, "IS_WINDOWS", False)
monkeypatch.setattr(serve, "has_module", lambda name: name == "uvloop")
assert serve.default_loop("granian") == "uvloop"
monkeypatch.setattr(serve, "has_module", lambda name: False)
assert serve.default_loop("granian") == "asyncio"
def test_default_loop_uvicorn(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(serve, "IS_WINDOWS", False)
monkeypatch.setattr(serve, "has_module", lambda name: name == "uvloop")
assert serve.default_loop("uvicorn") == "uvloop"
# Windows 上 uvicorn 只有 asyncio(即使装了 winloop 也不会用)
monkeypatch.setattr(serve, "IS_WINDOWS", True)
monkeypatch.setattr(serve, "has_module", lambda name: name == "winloop")
assert serve.default_loop("uvicorn") == "asyncio"
def test_default_loop_windows_without_winloop(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(serve, "IS_WINDOWS", True)
monkeypatch.setattr(serve, "has_module", lambda name: False)
assert serve.default_loop("granian") == "asyncio"
def test_resolve_loop_auto_uses_platform_default(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(serve, "IS_WINDOWS", False)
monkeypatch.setattr(serve, "has_module", lambda name: name in ("rloop", "uvloop"))
assert serve.resolve_loop("granian", "auto") == "rloop"
assert serve.resolve_loop("uvicorn", "auto") == "uvloop"
def test_build_command_granian(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(serve, "has_module", lambda name: True)
command = serve.build_command("granian", host="0.0.0.0", port=16688, workers=2, loop="winloop", reload=True)