chore(lint): clear the last violation of 11 strict ruff rules

Each of these rules sat at a limit of 1 in ruff-strict-budget.json, 2 for
B004, so a single lingering violation apiece was all that kept them
ratcheted instead of hard-failing. Clearing them takes each to zero, which
is what the header of ruff.toml describes as the precondition for moving a
rule into extend-select

Three carry real behavior. B004 rewrites two getattr(x, "__call__") probes
as callable(x). Ruff's own hint says to replace the whole expression with
callable(), which would be wrong here, because both sites need the __call__
object itself to hand to inspect.iscoroutinefunction. callable() resolves
__call__ on the type the way an actual call does, so an object carrying
__call__ as an instance attribute is no longer reported as an async
callable when calling it would raise TypeError

B021 drops an f prefix from JavelinGuardrail.__init__'s docstring. Python
evaluated it as an f-string expression and threw it away, so __doc__ was
None. PLR0206 removes an api_version parameter from a property getter on
AzureFoundryModelInfo: attribute access cannot pass arguments, so that
parameter was unreachable and read as None every time

The rest preserve behavior exactly. A dict comprehension, a generator
handed to any(), != over not ==, a plain boolean over a conditional
expression, a constant over None or "success", math.isfinite over a NaN
self-comparison paired with an inf membership test, a loop variable that no
longer shadows the enclosing model argument, and a dead
sys.version_info >= (3, 8) branch under a >=3.10 floor

B019 and N999 are left for separate PRs. B019 needs the lru_cache moved off
the method onto a per-instance cache, and four test files assert on
cache_info. N999 renames litellm/proxy/lambda.py, which deployment config
points at

ruff-strict-budget.json is deliberately untouched, per CLAUDE.md
This commit is contained in:
Saatvik Sawarn 2026-09-08 12:17:38 +05:30
parent 9dbfb060bd
commit c3fc8aedb7
13 changed files with 34 additions and 25 deletions

View file

@ -4,18 +4,10 @@ imported_openAIResponse = True
try:
import io
import logging
import sys
from typing import Any, TypeVar
from typing import Any, Literal, Protocol, TypeVar
from wandb.sdk.data_types import trace_tree
if sys.version_info >= (3, 8):
from typing import Literal, Protocol
else:
from typing import Literal
from typing_extensions import Protocol
logger: Final = logging.getLogger(__name__)
K = TypeVar("K", bound=str)

View file

@ -36,9 +36,8 @@ class CoroutineChecker:
target = callback
if not inspect.isfunction(target) and not inspect.ismethod(target):
try:
call_attr: Final = getattr(target, "__call__", None)
if call_attr is not None:
target = call_attr
if callable(target):
target = target.__call__
except Exception:
pass

View file

@ -1756,7 +1756,7 @@ def convert_to_anthropic_tool_invoke(
anthropic_tool_invoke: Final[list[AnthropicMessagesToolUseParam | dict[str, object]]] = []
for tool in tool_calls:
if not get_attribute_or_key(tool, "type") == "function":
if get_attribute_or_key(tool, "type") != "function":
continue
tool_id = cast(str, get_attribute_or_key(tool, "id"))

View file

@ -414,7 +414,7 @@ def token_counter(
params: Final = _MessageCountParams(model, custom_tokenizer)
num_tokens = _count_messages(params, new_messages, use_default_image_token_count, default_token_count)
if count_response_tokens is False:
includes_system_message: Final = any([message.get("role", None) == "system" for message in new_messages])
includes_system_message: Final = any(message.get("role", None) == "system" for message in new_messages)
num_tokens += _count_extra(params.count_function, tools, tool_choice, includes_system_message)
else:

View file

@ -138,9 +138,8 @@ class AzureFoundryModelInfo(BaseLLMModelInfo):
return api_key or litellm.api_key or get_secret_str("AZURE_AI_API_KEY")
@property
def api_version(self, api_version: str | None = None) -> str | None:
api_version = api_version or litellm.api_version or get_secret_str("AZURE_API_VERSION")
return api_version
def api_version(self) -> str | None:
return litellm.api_version or get_secret_str("AZURE_API_VERSION")
def get_token_counter(self) -> BaseTokenCounter | None:
"""

View file

@ -44,7 +44,7 @@ class JavelinGuardrail(CustomGuardrail):
application: str | None = None,
**kwargs,
):
f"""
"""
Initialize the JavelinGuardrail class.
This calls: {api_base}/{api_version}/guardrail/{guardrail_name}/apply

View file

@ -15,7 +15,7 @@ def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail"
# We check the raw guardrail dict because LitellmParams normalizes None → False,
# making it impossible to distinguish "not set" from "explicitly false" via litellm_params.
_raw_default_on: Final = cast(dict[str, Any], guardrail).get("litellm_params", {}).get("default_on")
_default_on: Final = False if _raw_default_on is False else True
_default_on: Final = _raw_default_on is not False
_callback: Final = MCPEndUserPermissionGuardrail(
guardrail_name=guardrail.get("guardrail_name", ""),

View file

@ -116,7 +116,8 @@ async def run_team_metadata_validation(
},
)
if not (
inspect.iscoroutinefunction(validator) or inspect.iscoroutinefunction(getattr(validator, "__call__", None))
inspect.iscoroutinefunction(validator)
or (callable(validator) and inspect.iscoroutinefunction(validator.__call__))
):
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,

View file

@ -152,7 +152,7 @@ def _get_spend_logs_metadata(
requester_ip_address=None,
additional_usage_values=None,
applied_guardrails=None,
status=None or "success",
status="success",
error_information=None,
proxy_server_request=None,
batch_models=None,

View file

@ -4,6 +4,7 @@ import copy
import hashlib
import inspect
import json
import math
import os
import smtplib
import ssl
@ -5887,7 +5888,7 @@ class PrismaClient:
return None
try:
value: Final = float(response_time_ms)
return value if value == value and value not in (float("inf"), float("-inf")) else None
return value if math.isfinite(value) else None
except (ValueError, TypeError):
verbose_proxy_logger.warning("Invalid response_time_ms value: %s", response_time_ms)
return None

View file

@ -4024,9 +4024,9 @@ class Router:
except KeyError:
pass
for model in models:
for model_name in models:
task = asyncio.create_task(
_async_completion_no_exceptions(model=model, messages=messages, stream=stream, **kwargs)
_async_completion_no_exceptions(model=model_name, messages=messages, stream=stream, **kwargs)
)
pending_tasks.append(task)

View file

@ -6188,7 +6188,7 @@ def function_to_dict(input_function) -> dict:
"enum": param_enum,
}
parameters[param_name] = dict([(k, v) for k, v in param_dict.items() if isinstance(v, str)])
parameters[param_name] = {k: v for k, v in param_dict.items() if isinstance(v, str)}
# Check if the parameter has no default value (i.e., it's required)
if param.default == param.empty:

View file

@ -84,6 +84,23 @@ class TestCoroutineChecker:
assert self.checker.is_async_callable(obj.sync_method) is False
assert self.checker.is_async_callable(SyncCallable()) is False
def test_instance_attribute_dunder_call_is_not_callable(self):
"""An async __call__ set on the instance does not make the object callable."""
async def async_func():
return "async"
class NotCallable:
pass
obj = NotCallable()
obj.__call__ = async_func # readable attribute, but never used for a call
with pytest.raises(TypeError):
obj()
assert self.checker.is_async_callable(obj) is False
def test_is_async_callable_caching(self):
"""Test that is_async_callable caches callable objects."""