Merge pull request #36336 from BerriAI/litellm_/standard-lists-api-d1dc4a

test(proxy): guard management_v1 against fastapi names removed in supported releases
This commit is contained in:
yuneng-jiang 2026-08-10 13:39:59 -07:00 committed by GitHub
commit 3726bceb53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,11 +1,18 @@
import ast
import importlib.util
from pathlib import Path
from types import ModuleType
from typing import Annotated
import fastapi.dependencies.utils as fastapi_dependency_utils
import pytest
from fastapi import Depends, FastAPI, Header, Query, Request
from fastapi.testclient import TestClient
import litellm.proxy.management_endpoints.management_v1.common as common_module
from litellm.proxy.management_endpoints.management_v1.common import (
ManagementProblem,
PROBLEM_CONTENT_TYPE,
ManagementProblem,
_declared_query_params,
problem_response,
reject_unknown_query_params,
@ -93,3 +100,57 @@ def test_declared_query_params_is_empty_when_the_route_has_no_dependant():
}
)
assert _declared_query_params(request) == frozenset()
# fastapi removed these in 0.140.7, which `pyproject.toml` still allows via
# `fastapi>=0.136.3,<1.0`. Add a name here whenever a supported release drops one.
FASTAPI_NAMES_REMOVED_IN_0_140_7 = frozenset({"get_flat_dependant"})
MANAGEMENT_V1_PACKAGE = Path(str(common_module.__file__)).parent
def _public_names(module: ModuleType) -> frozenset[str]:
return frozenset(name for name in vars(module) if not name.startswith("_"))
def _fastapi_names_imported_by(source_file: Path) -> frozenset[str]:
tree = ast.parse(source_file.read_text())
return frozenset(
alias.name
for node in ast.walk(tree)
if isinstance(node, ast.ImportFrom) and (node.module or "").startswith("fastapi")
for alias in node.names
)
@pytest.mark.parametrize(
"source_file", sorted(MANAGEMENT_V1_PACKAGE.glob("*.py")), ids=lambda path: path.name
)
def test_no_module_imports_a_fastapi_name_removed_in_a_supported_release(source_file: Path):
"""`pyproject.toml` allows fastapi up to <1.0, but CI only ever resolves 0.136.3.
Every other test here passes just as well against a module importing a name
fastapi has since deleted, because the pinned fastapi still has it. On a user's
fastapi>=0.140.7 that import is an ImportError, and `proxy_server` imports this
package unguarded at module level, so it takes the whole proxy down rather than
just these routes. Globbing the package means a new module is covered on sight.
"""
assert not _fastapi_names_imported_by(source_file) & FASTAPI_NAMES_REMOVED_IN_0_140_7
def test_common_still_imports_when_fastapi_has_dropped_those_names(monkeypatch: pytest.MonkeyPatch):
"""The static check above cannot prove the module actually loads; this does.
Behaviour cannot be asserted under the same simulation: on 0.136.3
`get_flat_params` calls `get_flat_dependant` internally, so it raises NameError
once the name is gone. Loading is the part this pins.
"""
for name in FASTAPI_NAMES_REMOVED_IN_0_140_7:
monkeypatch.delattr(fastapi_dependency_utils, name, raising=False)
spec = importlib.util.spec_from_file_location(
"management_v1_common__simulated_fastapi", Path(str(common_module.__file__))
)
assert spec is not None and spec.loader is not None
reimported = importlib.util.module_from_spec(spec)
spec.loader.exec_module(reimported)
assert _public_names(reimported) == _public_names(common_module)