mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-29 06:33:28 +00:00
fix(proxy): keep the original traceback when a reloaded worker fails to import the app
This commit is contained in:
parent
c43fff79ab
commit
0a2b8da1bf
2 changed files with 33 additions and 0 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import importlib
|
||||
import logging
|
||||
import sys
|
||||
from collections.abc import Callable, Sequence
|
||||
|
|
@ -48,7 +49,9 @@ def multi_worker_reload_supported() -> bool:
|
|||
|
||||
|
||||
def load_app_or_exit(import_path: str) -> Callable[..., object]:
|
||||
module_name: Final = import_path.partition(":")[0]
|
||||
try:
|
||||
importlib.import_module(module_name)
|
||||
return ASGI_APP.validate_python(import_path)
|
||||
except Exception:
|
||||
LOGGER.exception("Error loading ASGI app %s", import_path)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import contextlib
|
||||
import importlib
|
||||
import logging
|
||||
import logging.handlers
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
|
|
@ -7,6 +9,7 @@ import socket
|
|||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
|
@ -84,6 +87,33 @@ def test_a_broken_app_exits_the_worker_as_a_startup_failure(
|
|||
assert exit_info.value.code == uvicorn_config.STARTUP_FAILURE
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("module", "source"),
|
||||
(
|
||||
("reload_import_error_app", "import os\nfrom os import missing_name\n"),
|
||||
("reload_value_error_app", "import os\nraise ValueError('broken edit')\n"),
|
||||
),
|
||||
)
|
||||
def test_a_broken_import_logs_the_line_that_raised(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, module: str, source: str
|
||||
) -> None:
|
||||
app_file: Final = tmp_path / f"{module}.py"
|
||||
app_file.write_text(source)
|
||||
monkeypatch.syspath_prepend(str(tmp_path))
|
||||
uvicorn_logger: Final = logging.getLogger("uvicorn.error")
|
||||
captured: Final = logging.handlers.BufferingHandler(capacity=POOL_WORKERS)
|
||||
uvicorn_logger.addHandler(captured)
|
||||
try:
|
||||
with pytest.raises(SystemExit):
|
||||
load_app_or_exit(f"{module}:app")
|
||||
finally:
|
||||
uvicorn_logger.removeHandler(captured)
|
||||
(record,) = captured.buffer
|
||||
assert record.exc_info is not None
|
||||
raised_at: Final = frozenset((frame.filename, frame.lineno) for frame in traceback.extract_tb(record.exc_info[2]))
|
||||
assert (str(app_file), 2) in raised_at, record.exc_text
|
||||
|
||||
|
||||
def test_a_working_app_loads_as_the_module_defines_it(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
(tmp_path / "reload_working_app.py").write_text("async def app(scope, receive, send):\n return None\n")
|
||||
monkeypatch.syspath_prepend(str(tmp_path))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue