Enable PERF and T20 ruff rules (#3845)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bill Easton 2026-04-13 19:07:32 -05:00 committed by William Easton
commit f248845133
No known key found for this signature in database
10 changed files with 45 additions and 38 deletions

View file

@ -180,6 +180,7 @@ error-on-warning = true
fixable = ["ALL"]
ignore = [
"COM812",
"PERF203", # try-except in loop — all existing hits are intentional (retry loops, error skipping)
"PLR0913", # Too many arguments, MCP Servers have a lot of arguments, OKAY?!
"SIM102", # Dont require combining if statements
]
@ -194,12 +195,14 @@ extend-select = [
"INP", # flake8-no-pep420: Require __init__.py in namespace packages
"ISC", # flake8-implicit-str-concat: Prevent accidental string concatenation
"LOG", # flake8-logging: Catches logging module misuse
"PERF", # perflint: Performance anti-patterns (unnecessary copies, allocations)
"PIE", # flake8-pie: More idiomatic Python code
"PLE", # pylint-error: Catches actual errors (invalid operations, syntax issues)
"RSE", # flake8-raise: Unnecessary parentheses on raise
"RUF", # Ruff-specific: Modern best practices unique to Ruff
"SIM", # flake8-simplify: Simplifies verbose code patterns
"SLOT", # flake8-slots: Enforce __slots__ where applicable
"T20", # flake8-print: Catch accidental print() in library code
"TID", # flake8-tidy-imports: Banned imports and relative import enforcement
"UP", # pyupgrade: Modernize syntax for newer Python versions
]
@ -211,6 +214,10 @@ known-first-party = ["fastmcp"]
"__init__.py" = ["F401", "I001", "RUF013"]
# allow imports not at the top of the file
"src/fastmcp/__init__.py" = ["E402"]
# CLI and example code legitimately uses print() for user-facing output
"src/fastmcp/cli/**.py" = ["T20"]
"src/fastmcp/client/oauth_callback.py" = ["T20"]
"src/fastmcp/contrib/**/example.py" = ["T20"]
"!src/**.py" = [ # Only enforce extended ruff rules for code in src/
"B", # flake8-bugbear
"C4", # flake8-comprehensions
@ -221,12 +228,14 @@ known-first-party = ["fastmcp"]
"INP", # flake8-no-pep420
"ISC", # flake8-implicit-str-concat
"LOG", # flake8-logging
"PERF", # perflint
"PIE", # flake8-pie
"PLE", # pylint-error
"RSE", # flake8-raise
"RUF", # Ruff-specific
"SIM", # flake8-simplify
"SLOT", # flake8-slots
"T20", # flake8-print
"TID", # flake8-tidy-imports
]

View file

@ -261,7 +261,7 @@ def _tool_function_source(tool: mcp.types.Tool) -> str:
# Build call arguments, using parsed versions for JSON params
call_arg_parts = []
for prop_name, _ in properties.items():
for prop_name in properties:
safe_name = _to_python_identifier(prop_name)
if any(pn == prop_name for pn, _ in json_params):
call_arg_parts.append(f"{prop_name!r}: {safe_name}_parsed")
@ -313,8 +313,7 @@ def generate_cli_script(
lines.append("from rich.console import Console")
lines.append("")
lines.append("from fastmcp import Client")
for imp in sorted(extra_imports):
lines.append(imp)
lines.extend(sorted(extra_imports))
lines.append("")
# --- Transport config ---
@ -506,8 +505,7 @@ def generate_cli_script(
"# ---------------------------------------------------------------------------"
)
for tool in tools:
lines.append(_tool_function_source(tool))
lines.extend(_tool_function_source(tool) for tool in tools)
# --- Entry point ---
lines.append("")

View file

@ -47,8 +47,7 @@ def generate_goose_deeplink(
extension_id = _slugify(name)
params: list[str] = [f"cmd={quote(command, safe='')}"]
for arg in args:
params.append(f"arg={quote(arg, safe='')}")
params.extend(f"arg={quote(arg, safe='')}" for arg in args)
params.append(f"id={quote(extension_id, safe='')}")
params.append(f"name={quote(name, safe='')}")
params.append(f"description={quote(description, safe='')}")

View file

@ -216,12 +216,11 @@ class AnthropicSamplingHandler:
# Extract text content from the result
result_content: str | list[TextBlockParam] = ""
if item.content:
text_blocks: list[TextBlockParam] = []
for sub_item in item.content:
if isinstance(sub_item, TextContent):
text_blocks.append(
TextBlockParam(type="text", text=sub_item.text)
)
text_blocks: list[TextBlockParam] = [
TextBlockParam(type="text", text=sub_item.text)
for sub_item in item.content
if isinstance(sub_item, TextContent)
]
if len(text_blocks) == 1:
result_content = text_blocks[0]["text"]
elif text_blocks:
@ -270,12 +269,11 @@ class AnthropicSamplingHandler:
if isinstance(content, ToolResultContent):
result_content_str: str | list[TextBlockParam] = ""
if content.content:
text_parts: list[TextBlockParam] = []
for item in content.content:
if isinstance(item, TextContent):
text_parts.append(
TextBlockParam(type="text", text=item.text)
)
text_parts: list[TextBlockParam] = [
TextBlockParam(type="text", text=item.text)
for item in content.content
if isinstance(item, TextContent)
]
if len(text_parts) == 1:
result_content_str = text_parts[0]["text"]
elif text_parts:

View file

@ -280,9 +280,9 @@ def _convert_messages_to_google_genai_content(
# Handle list content (tool calls + results)
if isinstance(content, list):
parts: list[Part] = []
for item in content:
parts.append(_sampling_content_to_google_genai_part(item))
parts: list[Part] = [
_sampling_content_to_google_genai_part(item) for item in content
]
if message.role == "user":
google_messages.append(UserContent(parts=parts))

View file

@ -231,10 +231,11 @@ class OpenAISamplingHandler:
# Collect tool results (added after assistant message)
content_text = ""
if item.content:
result_texts = []
for sub_item in item.content:
if isinstance(sub_item, TextContent):
result_texts.append(sub_item.text)
result_texts = [
sub_item.text
for sub_item in item.content
if isinstance(sub_item, TextContent)
]
content_text = "\n".join(result_texts)
tool_messages.append(
ChatCompletionToolMessageParam(

View file

@ -173,9 +173,11 @@ def _walk_prefab_tools(server: FastMCP) -> list[Tool]:
if isinstance(inner, FastMCPApp):
sources.append(inner._local)
for src in sources:
for component in src._components.values():
if isinstance(component, Tool) and _is_prefab_tool(component):
results.append(component)
results.extend(
component
for component in src._components.values()
if isinstance(component, Tool) and _is_prefab_tool(component)
)
# Recurse into aggregate children
from fastmcp.server.providers.aggregate import AggregateProvider

View file

@ -104,7 +104,7 @@ class PromptsAsTools(Transform):
result: list[dict[str, Any]] = []
for p in prompts:
result.append(
result.append( # noqa: PERF401
{
"name": p.name,
"description": p.description,

View file

@ -110,7 +110,7 @@ class ResourcesAsTools(Transform):
result: list[dict[str, Any]] = []
for r in resources:
result.append(
result.append( # noqa: PERF401
{
"uri": str(r.uri),
"name": r.name,
@ -120,7 +120,7 @@ class ResourcesAsTools(Transform):
)
for t in templates:
result.append(
result.append( # noqa: PERF401
{
"uri_template": t.uri_template,
"name": t.name,

View file

@ -136,7 +136,7 @@ async def inspect_fastmcp_v2(mcp: FastMCP[Any]) -> FastMCPInfo:
# Extract detailed prompt information
prompt_infos = []
for prompt in prompts_list:
prompt_infos.append(
prompt_infos.append( # noqa: PERF401
PromptInfo(
key=prompt.key,
name=prompt.name or prompt.key,
@ -156,7 +156,7 @@ async def inspect_fastmcp_v2(mcp: FastMCP[Any]) -> FastMCPInfo:
# Extract detailed resource information
resource_infos = []
for resource in resources_list:
resource_infos.append(
resource_infos.append( # noqa: PERF401
ResourceInfo(
key=resource.key,
uri=str(resource.uri),
@ -178,7 +178,7 @@ async def inspect_fastmcp_v2(mcp: FastMCP[Any]) -> FastMCPInfo:
# Extract detailed template information
template_infos = []
for template in templates_list:
template_infos.append(
template_infos.append( # noqa: PERF401
TemplateInfo(
key=template.key,
uri_template=template.uri_template,
@ -258,7 +258,7 @@ async def inspect_fastmcp_v1(mcp: FastMCP1x) -> FastMCPInfo:
# Extract detailed tool information from MCP Tool objects
tool_infos = []
for mcp_tool in mcp_tools:
tool_infos.append(
tool_infos.append( # noqa: PERF401
ToolInfo(
key=mcp_tool.name,
name=mcp_tool.name,
@ -301,7 +301,7 @@ async def inspect_fastmcp_v1(mcp: FastMCP1x) -> FastMCPInfo:
# Extract detailed resource information from MCP Resource objects
resource_infos = []
for mcp_resource in mcp_resources:
resource_infos.append(
resource_infos.append( # noqa: PERF401
ResourceInfo(
key=str(mcp_resource.uri),
uri=str(mcp_resource.uri),
@ -321,7 +321,7 @@ async def inspect_fastmcp_v1(mcp: FastMCP1x) -> FastMCPInfo:
# Extract detailed template information from MCP ResourceTemplate objects
template_infos = []
for mcp_template in mcp_templates:
template_infos.append(
template_infos.append( # noqa: PERF401
TemplateInfo(
key=str(mcp_template.uriTemplate),
uri_template=str(mcp_template.uriTemplate),