chore: import upstream snapshot with attribution
Upgrade checks / Notify on failure (push) Has been cancelled
Upgrade checks / Close issue on success (push) Has been cancelled
Schema Crash Test / Real-world schema crash test (232K schemas) (push) Has been cancelled
Run static analysis / static_analysis (push) Has been cancelled
Tests / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Tests / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Tests / Tests with lowest-direct dependencies (push) Has been cancelled
Tests / MCP conformance tests (push) Has been cancelled
Tests / Integration tests (push) Has been cancelled
Tests / Package install smoke (push) Has been cancelled
Upgrade checks / Static analysis (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.13 on ubuntu-latest (push) Has been cancelled
Upgrade checks / Tests: Python 3.10 on windows-latest (push) Has been cancelled
Upgrade checks / Integration tests (push) Has been cancelled
Update MCPServerConfig Schema / update-config-schema (push) Has been cancelled
Update SDK Documentation / update-sdk-docs (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:39:59 +08:00
commit 60e0ffc959
1282 changed files with 294901 additions and 0 deletions
+371
View File
@@ -0,0 +1,371 @@
#!/usr/bin/env python
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "httpx",
# ]
# ///
"""
Auto-close duplicate GitHub issues.
This script runs on a schedule to automatically close issues that have been
marked as duplicates and haven't received any preventing activity.
"""
import os
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
import httpx
@dataclass
class Issue:
"""Represents a GitHub issue."""
number: int
title: str
state: str
created_at: str
user_id: int
user_login: str
@dataclass
class Comment:
"""Represents a GitHub comment."""
id: int
body: str
created_at: str
user_id: int
user_login: str
user_type: str
@dataclass
class Reaction:
"""Represents a reaction on a comment."""
user_id: int
user_login: str
content: str
class GitHubClient:
"""Client for interacting with GitHub API."""
def __init__(self, token: str, owner: str, repo: str):
self.token = token
self.owner = owner
self.repo = repo
self.headers = {
"Authorization": f"token {token}",
"Accept": "application/vnd.github.v3+json",
}
self.base_url = f"https://api.github.com/repos/{owner}/{repo}"
def get_potential_duplicate_issues(self) -> list[Issue]:
"""Fetch open issues with the potential-duplicate label."""
url = f"{self.base_url}/issues"
issues = []
with httpx.Client() as client:
page = 1
while page <= 10: # Safety limit
response = client.get(
url,
headers=self.headers,
params={
"state": "open",
"labels": "potential-duplicate",
"per_page": 100,
"page": page,
},
)
if response.status_code != 200:
print(f"Error fetching issues: {response.status_code}")
break
data = response.json()
if not data:
break
for item in data:
# Skip pull requests
if "pull_request" in item:
continue
issues.append(
Issue(
number=item["number"],
title=item["title"],
state=item["state"],
created_at=item["created_at"],
user_id=item["user"]["id"],
user_login=item["user"]["login"],
)
)
page += 1
return issues
def get_issue_comments(self, issue_number: int) -> list[Comment]:
"""Fetch all comments for an issue."""
url = f"{self.base_url}/issues/{issue_number}/comments"
comments = []
with httpx.Client() as client:
page = 1
while True:
response = client.get(
url, headers=self.headers, params={"page": page, "per_page": 100}
)
if response.status_code != 200:
break
data = response.json()
if not data:
break
for comment_data in data:
comments.append(
Comment(
id=comment_data["id"],
body=comment_data["body"],
created_at=comment_data["created_at"],
user_id=comment_data["user"]["id"],
user_login=comment_data["user"]["login"],
user_type=comment_data["user"]["type"],
)
)
page += 1
if page > 10: # Safety limit
break
return comments
def get_comment_reactions(
self, issue_number: int, comment_id: int
) -> list[Reaction]:
"""Fetch reactions for a specific comment."""
url = f"{self.base_url}/issues/{issue_number}/comments/{comment_id}/reactions"
reactions = []
with httpx.Client() as client:
response = client.get(url, headers=self.headers)
if response.status_code != 200:
return reactions
data = response.json()
for reaction_data in data:
reactions.append(
Reaction(
user_id=reaction_data["user"]["id"],
user_login=reaction_data["user"]["login"],
content=reaction_data["content"],
)
)
return reactions
def remove_label(self, issue_number: int, label: str) -> bool:
"""Remove a label from an issue."""
url = f"{self.base_url}/issues/{issue_number}/labels/{label}"
with httpx.Client() as client:
response = client.delete(url, headers=self.headers)
return response.status_code in [200, 204]
def close_issue(self, issue_number: int, comment: str) -> bool:
"""Close an issue with a comment and add duplicate label."""
# First add the comment
comment_url = f"{self.base_url}/issues/{issue_number}/comments"
with httpx.Client() as client:
response = client.post(
comment_url, headers=self.headers, json={"body": comment}
)
if response.status_code != 201:
print(f"Failed to add comment to issue #{issue_number}")
return False
# Swap labels: remove potential-duplicate, add duplicate
self.remove_label(issue_number, "potential-duplicate")
labels_url = f"{self.base_url}/issues/{issue_number}/labels"
with httpx.Client() as client:
response = client.post(
labels_url, headers=self.headers, json={"labels": ["duplicate"]}
)
if response.status_code not in [200, 201]:
print(f"Failed to add duplicate label to issue #{issue_number}")
# Then close the issue
issue_url = f"{self.base_url}/issues/{issue_number}"
with httpx.Client() as client:
response = client.patch(
issue_url, headers=self.headers, json={"state": "closed"}
)
return response.status_code == 200
def find_duplicate_comment(comments: list[Comment]) -> Comment | None:
"""Find a bot comment marking the issue as duplicate."""
for comment in comments:
# Check for the specific duplicate message format from a bot
if (
comment.user_type == "Bot"
and "possible duplicate issues" in comment.body.lower()
):
return comment
return None
def was_already_auto_closed(comments: list[Comment]) -> bool:
"""Check if this issue was already auto-closed once."""
for comment in comments:
if (
comment.user_type == "Bot"
and "closing this issue as a duplicate" in comment.body.lower()
):
return True
return False
def is_past_cooldown(duplicate_comment: Comment) -> bool:
"""Check if the 3-day cooldown period has passed."""
comment_date = datetime.fromisoformat(
duplicate_comment.created_at.replace("Z", "+00:00")
)
three_days_ago = datetime.now(timezone.utc) - timedelta(days=3)
return comment_date <= three_days_ago
def has_human_activity(
issue: Issue,
duplicate_comment: Comment,
all_comments: list[Comment],
reactions: list[Reaction],
) -> bool:
"""Check if there's human activity that should prevent auto-closure."""
comment_date = datetime.fromisoformat(
duplicate_comment.created_at.replace("Z", "+00:00")
)
# Check for preventing reactions (thumbs down)
for reaction in reactions:
if reaction.content in ["-1", "confused"]:
print(
f"Issue #{issue.number}: Has preventing reaction from {reaction.user_login}"
)
return True
# Check for any human comment after the duplicate marking
for comment in all_comments:
comment_date_check = datetime.fromisoformat(
comment.created_at.replace("Z", "+00:00")
)
if comment_date_check > comment_date:
if comment.user_type != "Bot":
print(
f"Issue #{issue.number}: {comment.user_login} commented after duplicate marking"
)
return True
return False
def main():
"""Main entry point for auto-closing duplicate issues."""
print("[DEBUG] Starting auto-close duplicates script")
# Get environment variables
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable is required")
owner = os.environ.get("GITHUB_REPOSITORY_OWNER", "prefecthq")
repo = os.environ.get("GITHUB_REPOSITORY_NAME", "fastmcp")
print(f"[DEBUG] Repository: {owner}/{repo}")
# Initialize client
client = GitHubClient(token, owner, repo)
# Only fetch issues with the potential-duplicate label
all_issues = client.get_potential_duplicate_issues()
print(f"[DEBUG] Found {len(all_issues)} open issues with potential-duplicate label")
closed_count = 0
cleared_count = 0
for issue in all_issues:
# Get comments for this issue
comments = client.get_issue_comments(issue.number)
# Look for duplicate marking comment
duplicate_comment = find_duplicate_comment(comments)
if not duplicate_comment:
# Label exists but no bot comment - clean up the label
print(f"[DEBUG] Issue #{issue.number} has label but no duplicate comment")
client.remove_label(issue.number, "potential-duplicate")
cleared_count += 1
continue
# Skip if already auto-closed once (someone reopened it intentionally)
if was_already_auto_closed(comments):
print(
f"[DEBUG] Issue #{issue.number} was already auto-closed, removing label"
)
client.remove_label(issue.number, "potential-duplicate")
cleared_count += 1
continue
print(f"[DEBUG] Issue #{issue.number} has duplicate comment")
# Still in cooldown period - skip for now, check again later
if not is_past_cooldown(duplicate_comment):
print(f"[DEBUG] Issue #{issue.number} still in 3-day cooldown period")
continue
# Get reactions on the duplicate comment
reactions = client.get_comment_reactions(issue.number, duplicate_comment.id)
# Check for human activity that prevents closure
if has_human_activity(issue, duplicate_comment, comments, reactions):
print(f"[DEBUG] Issue #{issue.number} has human activity, removing label")
client.remove_label(issue.number, "potential-duplicate")
cleared_count += 1
continue
# No human activity after cooldown - close as duplicate
close_message = (
"Closing this issue as a duplicate based on the automated analysis above.\n\n"
"The duplicate issues identified contain existing discussions and potential solutions. "
"Please add your 👍 to those issues if they match your use case.\n\n"
"If this was closed in error, please leave a comment explaining why this is not "
"a duplicate and we'll reopen it."
)
if client.close_issue(issue.number, close_message):
print(f"[SUCCESS] Closed issue #{issue.number} as duplicate")
closed_count += 1
else:
print(f"[ERROR] Failed to close issue #{issue.number}")
print(
f"[DEBUG] Processing complete. Closed {closed_count} duplicates, "
f"cleared {cleared_count} from review"
)
if __name__ == "__main__":
main()
+415
View File
@@ -0,0 +1,415 @@
#!/usr/bin/env python
# /// script
# requires-python = ">=3.10"
# dependencies = [
# "httpx",
# ]
# ///
"""
Auto-close issues that need MRE (Minimal Reproducible Example).
This script runs on a schedule to automatically close issues that have been
marked as "needs MRE" and haven't received activity from the issue author
within 7 days.
"""
import os
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
import httpx
MAINTAINER_AUTHOR_ASSOCIATIONS = {"OWNER", "MEMBER", "COLLABORATOR"}
@dataclass
class Issue:
"""Represents a GitHub issue."""
number: int
title: str
state: str
created_at: str
user_id: int
user_login: str
body: str | None
author_association: str
@dataclass
class Comment:
"""Represents a GitHub comment."""
id: int
body: str
created_at: str
user_id: int
user_login: str
@dataclass
class Event:
"""Represents a GitHub issue event."""
event: str
created_at: str
label_name: str | None
class GitHubClient:
"""Client for interacting with GitHub API."""
def __init__(self, token: str, owner: str, repo: str):
self.token = token
self.owner = owner
self.repo = repo
self.headers = {
"Authorization": f"token {token}",
"Accept": "application/vnd.github.v3+json",
}
self.base_url = f"https://api.github.com/repos/{owner}/{repo}"
def get_issues_with_label(
self, label: str, page: int = 1, per_page: int = 100
) -> list[Issue]:
"""Fetch open issues with a specific label."""
url = f"{self.base_url}/issues"
issues = []
with httpx.Client() as client:
response = client.get(
url,
headers=self.headers,
params={
"state": "open",
"labels": label,
"per_page": per_page,
"page": page,
},
)
if response.status_code != 200:
print(f"Error fetching issues: {response.status_code}")
return issues
data = response.json()
for item in data:
# Skip pull requests
if "pull_request" in item:
continue
issues.append(
Issue(
number=item["number"],
title=item["title"],
state=item["state"],
created_at=item["created_at"],
user_id=item["user"]["id"],
user_login=item["user"]["login"],
body=item.get("body"),
author_association=item.get("author_association", "NONE"),
)
)
return issues
def get_issue_events(self, issue_number: int) -> list[Event]:
"""Fetch all events for an issue."""
url = f"{self.base_url}/issues/{issue_number}/events"
events = []
with httpx.Client() as client:
page = 1
while True:
response = client.get(
url, headers=self.headers, params={"page": page, "per_page": 100}
)
if response.status_code != 200:
break
data = response.json()
if not data:
break
for event_data in data:
label_name = None
if event_data["event"] == "labeled" and "label" in event_data:
label_name = event_data["label"]["name"]
events.append(
Event(
event=event_data["event"],
created_at=event_data["created_at"],
label_name=label_name,
)
)
page += 1
if page > 10: # Safety limit
break
return events
def get_issue_comments(self, issue_number: int) -> list[Comment]:
"""Fetch all comments for an issue."""
url = f"{self.base_url}/issues/{issue_number}/comments"
comments = []
with httpx.Client() as client:
page = 1
while True:
response = client.get(
url, headers=self.headers, params={"page": page, "per_page": 100}
)
if response.status_code != 200:
break
data = response.json()
if not data:
break
for comment_data in data:
comments.append(
Comment(
id=comment_data["id"],
body=comment_data["body"],
created_at=comment_data["created_at"],
user_id=comment_data["user"]["id"],
user_login=comment_data["user"]["login"],
)
)
page += 1
if page > 10: # Safety limit
break
return comments
def get_issue_timeline(self, issue_number: int) -> list[dict]:
"""Fetch timeline events for an issue (includes issue edits)."""
url = f"{self.base_url}/issues/{issue_number}/timeline"
timeline = []
with httpx.Client() as client:
page = 1
while True:
response = client.get(
url,
headers={
**self.headers,
"Accept": "application/vnd.github.mockingbird-preview+json",
},
params={"page": page, "per_page": 100},
)
if response.status_code != 200:
break
data = response.json()
if not data:
break
timeline.extend(data)
page += 1
if page > 10: # Safety limit
break
return timeline
def close_issue(self, issue_number: int, comment: str) -> tuple[bool, bool]:
"""Close an issue with a comment.
Closes first, then comments — so a failed comment never leaves
a misleading "closing" notice on a still-open issue.
Returns (closed, commented) so the caller can log partial failures.
"""
# Close the issue first
issue_url = f"{self.base_url}/issues/{issue_number}"
with httpx.Client() as client:
response = client.patch(
issue_url, headers=self.headers, json={"state": "closed"}
)
if response.status_code != 200:
print(
f"Failed to close issue #{issue_number}: "
f"{response.status_code} {response.text}"
)
return False, False
# Then add the comment
comment_url = f"{self.base_url}/issues/{issue_number}/comments"
with httpx.Client() as client:
response = client.post(
comment_url, headers=self.headers, json={"body": comment}
)
if response.status_code != 201:
print(
f"Issue #{issue_number} was closed but comment failed: "
f"{response.status_code} {response.text}"
)
return True, False
return True, True
def find_label_application_date(
events: list[Event], label_name: str
) -> datetime | None:
"""Find when a specific label was applied to an issue."""
# Look for the most recent application of this label
for event in reversed(events):
if event.event == "labeled" and event.label_name == label_name:
return datetime.fromisoformat(event.created_at.replace("Z", "+00:00"))
return None
def has_author_activity_after(
issue: Issue,
comments: list[Comment],
timeline: list[dict],
after_date: datetime,
) -> bool:
"""Check if the issue author had any activity after a specific date."""
# Check for comments from author
for comment in comments:
if comment.user_id == issue.user_id:
comment_date = datetime.fromisoformat(
comment.created_at.replace("Z", "+00:00")
)
if comment_date > after_date:
print(
f"Issue #{issue.number}: Author commented after label application"
)
return True
# Check for issue body edits from author
for event in timeline:
if event.get("event") == "renamed" or event.get("event") == "edited":
if event.get("actor", {}).get("id") == issue.user_id:
event_date = datetime.fromisoformat(
event["created_at"].replace("Z", "+00:00")
)
if event_date > after_date:
print(
f"Issue #{issue.number}: Author edited issue after label application"
)
return True
return False
def should_close_as_needs_mre(
issue: Issue,
label_date: datetime,
comments: list[Comment],
timeline: list[dict],
) -> bool:
"""Determine if an issue should be closed for needing an MRE."""
if issue.author_association in MAINTAINER_AUTHOR_ASSOCIATIONS:
print(f"Issue #{issue.number}: Skipping maintainer-authored issue")
return False
# Check if label is old enough (7 days)
seven_days_ago = datetime.now(timezone.utc) - timedelta(days=7)
if label_date > seven_days_ago:
return False
# Check for author activity after the label was applied
if has_author_activity_after(issue, comments, timeline, label_date):
return False
return True
def main():
"""Main entry point for auto-closing needs MRE issues."""
print("[DEBUG] Starting auto-close needs MRE script")
# Get environment variables
token = os.environ.get("GITHUB_TOKEN")
if not token:
raise ValueError("GITHUB_TOKEN environment variable is required")
owner = os.environ.get("GITHUB_REPOSITORY_OWNER", "prefecthq")
repo = os.environ.get("GITHUB_REPOSITORY_NAME", "fastmcp")
print(f"[DEBUG] Repository: {owner}/{repo}")
# Initialize client
client = GitHubClient(token, owner, repo)
# Get issues with "needs MRE" label
all_issues = []
page = 1
while page <= 20: # Safety limit
issues = client.get_issues_with_label("needs MRE", page=page)
if not issues:
break
all_issues.extend(issues)
page += 1
print(f"[DEBUG] Found {len(all_issues)} open issues with 'needs MRE' label")
processed_count = 0
closed_count = 0
for issue in all_issues:
processed_count += 1
if processed_count % 10 == 0:
print(f"[DEBUG] Processed {processed_count}/{len(all_issues)} issues")
# Get events to find when label was applied
events = client.get_issue_events(issue.number)
label_date = find_label_application_date(events, "needs MRE")
if not label_date:
print(
f"[DEBUG] Issue #{issue.number}: Could not find label application date"
)
continue
print(
f"[DEBUG] Issue #{issue.number}: Label applied on {label_date.isoformat()}"
)
# Get comments and timeline
comments = client.get_issue_comments(issue.number)
timeline = client.get_issue_timeline(issue.number)
# Check if we should close
if should_close_as_needs_mre(issue, label_date, comments, timeline):
close_message = (
"This issue is being automatically closed because we requested a minimal reproducible example (MRE) "
"7 days ago and haven't received a response from the issue author.\n\n"
"**If you can provide an MRE**, please add it as a comment and we'll reopen this issue. "
"An MRE should be a complete, runnable code snippet that demonstrates the problem.\n\n"
"**If this was closed in error**, please leave a comment explaining the situation and we'll reopen it."
)
closed, commented = client.close_issue(issue.number, close_message)
if closed:
closed_count += 1
if commented:
print(f"[SUCCESS] Closed issue #{issue.number} (needs MRE)")
else:
print(
f"[WARNING] Closed issue #{issue.number} but "
f"comment was not posted"
)
else:
print(f"[ERROR] Failed to close issue #{issue.number}")
print(f"[DEBUG] Processing complete. Closed {closed_count} issues needing MRE")
if __name__ == "__main__":
main()
+212
View File
@@ -0,0 +1,212 @@
#!/usr/bin/env python
"""Benchmark import times for fastmcp and its dependency chain.
Each measurement runs in a fresh subprocess so there's no shared module cache.
Incremental costs are measured by pre-importing dependencies, so we can see
what each module truly adds.
Usage:
uv run python scripts/benchmark_imports.py
uv run python scripts/benchmark_imports.py --runs 10
uv run python scripts/benchmark_imports.py --json
"""
from __future__ import annotations
import argparse
import json
import subprocess
import sys
from dataclasses import dataclass
@dataclass
class BenchmarkCase:
label: str
stmt: str
prereqs: str = ""
group: str = ""
CASES = [
# --- Floor ---
BenchmarkCase("pydantic", "import pydantic", group="floor"),
BenchmarkCase("mcp", "import mcp", group="floor"),
BenchmarkCase(
"mcp (server only)", "import mcp.server.lowlevel.server", group="floor"
),
# --- Auth stack (incremental over mcp) ---
BenchmarkCase(
"authlib.jose", "import authlib.jose", prereqs="import mcp", group="auth"
),
BenchmarkCase(
"cryptography.fernet",
"from cryptography.fernet import Fernet",
prereqs="import mcp",
group="auth",
),
BenchmarkCase(
"authlib.integrations.httpx_client",
"from authlib.integrations.httpx_client import AsyncOAuth2Client",
prereqs="import mcp",
group="auth",
),
BenchmarkCase(
"key_value.aio", "import key_value.aio", prereqs="import mcp", group="auth"
),
BenchmarkCase(
"key_value.aio.stores.filetree",
"from key_value.aio.stores.filetree import FileTreeStore",
prereqs="import mcp",
group="auth",
),
BenchmarkCase("beartype", "import beartype", prereqs="import mcp", group="auth"),
# --- Docket stack (incremental over mcp) ---
BenchmarkCase("redis", "import redis", prereqs="import mcp", group="docket"),
BenchmarkCase(
"opentelemetry.sdk.metrics",
"import opentelemetry.sdk.metrics",
prereqs="import mcp",
group="docket",
),
BenchmarkCase("docket", "import docket", prereqs="import mcp", group="docket"),
BenchmarkCase("croniter", "import croniter", prereqs="import mcp", group="docket"),
# --- Other deps (incremental over mcp) ---
BenchmarkCase("httpx", "import httpx", prereqs="import mcp", group="other"),
BenchmarkCase(
"starlette",
"from starlette.applications import Starlette",
prereqs="import mcp",
group="other",
),
BenchmarkCase(
"pydantic_settings",
"import pydantic_settings",
prereqs="import mcp",
group="other",
),
BenchmarkCase(
"rich.console", "import rich.console", prereqs="import mcp", group="other"
),
BenchmarkCase("jsonref", "import jsonref", prereqs="import mcp", group="other"),
BenchmarkCase("requests", "import requests", prereqs="import mcp", group="other"),
# --- FastMCP (total and incremental) ---
BenchmarkCase("fastmcp (total)", "from fastmcp import FastMCP", group="fastmcp"),
BenchmarkCase(
"fastmcp (over mcp)",
"from fastmcp import FastMCP",
prereqs="import mcp",
group="fastmcp",
),
BenchmarkCase(
"fastmcp (over mcp+docket)",
"from fastmcp import FastMCP",
prereqs="import mcp; import docket",
group="fastmcp",
),
BenchmarkCase(
"fastmcp (over mcp+docket+auth deps)",
"from fastmcp import FastMCP",
prereqs=(
"import mcp; import docket; import authlib.jose;"
" from cryptography.fernet import Fernet;"
" import key_value.aio"
),
group="fastmcp",
),
]
def measure_once(stmt: str, prereqs: str) -> float | None:
pre = prereqs + "; " if prereqs else ""
code = (
f"{pre}"
"import time as _t; _s=_t.perf_counter(); "
f"{stmt}; "
"print(f'{(_t.perf_counter()-_s)*1000:.2f}')"
)
r = subprocess.run(
[sys.executable, "-c", code],
capture_output=True,
text=True,
timeout=30,
)
if r.returncode == 0 and r.stdout.strip():
return float(r.stdout.strip())
return None
def measure(case: BenchmarkCase, runs: int) -> dict[str, float | str | None]:
times: list[float] = []
for _ in range(runs):
t = measure_once(case.stmt, case.prereqs)
if t is not None:
times.append(t)
if not times:
return {"label": case.label, "group": case.group, "median_ms": None}
times.sort()
median = times[len(times) // 2]
return {
"label": case.label,
"group": case.group,
"median_ms": round(median, 1),
"min_ms": round(times[0], 1),
"max_ms": round(times[-1], 1),
"runs": len(times),
}
def print_table(results: list[dict[str, float | str | None]]) -> None:
current_group = None
print(f"\n{'Module':<45} {'Median':>8} {'Min':>8} {'Max':>8}")
print("-" * 71)
for r in results:
if r["group"] != current_group:
current_group = r["group"]
group_labels = {
"floor": "--- Unavoidable floor ---",
"auth": "--- Auth stack (incremental over mcp) ---",
"docket": "--- Docket stack (incremental over mcp) ---",
"other": "--- Other deps (incremental over mcp) ---",
"fastmcp": "--- FastMCP totals ---",
}
print(f"\n{group_labels.get(current_group, current_group)}")
if r["median_ms"] is not None:
print(
f" {r['label']:<43} {r['median_ms']:>7.1f}ms"
f" {r['min_ms']:>7.1f}ms {r['max_ms']:>7.1f}ms"
)
else:
print(f" {r['label']:<43} error")
def main() -> None:
parser = argparse.ArgumentParser(description="Benchmark fastmcp import times")
parser.add_argument(
"--runs", type=int, default=5, help="Number of runs per measurement (default 5)"
)
parser.add_argument("--json", action="store_true", help="Output results as JSON")
args = parser.parse_args()
print(f"Benchmarking import times ({args.runs} runs each)...")
print(f"Python: {sys.version.split()[0]}")
print(f"Executable: {sys.executable}")
results = []
for case in CASES:
r = measure(case, args.runs)
results.append(r)
if not args.json:
ms = f"{r['median_ms']:.1f}ms" if r["median_ms"] is not None else "error"
print(f" {case.label}: {ms}")
if args.json:
print(json.dumps(results, indent=2))
else:
print_table(results)
if __name__ == "__main__":
main()