Skip to content

Commit f8c6bcc

Browse files
committed
Format code with Black and isort
- Auto-format all 69 Python files with Black (line length: 120) - Sort imports with isort (black-compatible profile) - Ensures code meets repository formatting standards - Fixes CI code-quality workflow failures All files now pass black --check and isort --check-only
1 parent 77884cf commit f8c6bcc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+4440
-4792
lines changed

app/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
# Lazy import to avoid requiring FastAPI when only using models
1414
try:
1515
from .api.app import app
16+
1617
_has_fastapi = True
1718
except ImportError:
1819
app = None
1920
_has_fastapi = False
2021

2122
from .utils.config import *
2223

23-
__all__ = ["app"] + [name for name in dir() if not name.startswith('_')]
24+
__all__ = ["app"] + [name for name in dir() if not name.startswith("_")]

app/api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77

88
from .app import app
99

10-
__all__ = ["app"]
10+
__all__ = ["app"]

app/api/app.py

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
"""FastAPI application for BioAnalyzer backend API."""
2-
from fastapi import FastAPI, Request
3-
from fastapi.middleware.cors import CORSMiddleware
4-
from fastapi.responses import JSONResponse
5-
from fastapi.exceptions import RequestValidationError
2+
63
import logging
74
import os
85
import sys
96
import traceback
107

8+
from fastapi import FastAPI, Request
9+
from fastapi.exceptions import RequestValidationError
10+
from fastapi.middleware.cors import CORSMiddleware
11+
from fastapi.responses import JSONResponse
12+
1113
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
1214

13-
from app.api.routers import bugsigdb_analysis, bugsigdb_analysis_v2, system, study_analysis
1415
from app.api.middleware.rate_limit import RateLimitMiddleware
1516
from app.api.middleware.request_id import RequestIDMiddleware
17+
from app.api.routers import bugsigdb_analysis, bugsigdb_analysis_v2, study_analysis, system
1618
from app.utils.config import (
1719
CORS_ORIGINS,
1820
ENABLE_RATE_LIMITING,
19-
RATE_LIMIT_PER_MINUTE,
2021
ENABLE_REQUEST_ID,
2122
ENVIRONMENT,
22-
setup_logging
23+
RATE_LIMIT_PER_MINUTE,
24+
setup_logging,
2325
)
2426

2527
setup_logging()
@@ -39,13 +41,10 @@
3941
tags_metadata=[
4042
{
4143
"name": "BugSigDB Analysis",
42-
"description": "Core endpoints for analyzing papers for the 6 essential BugSigDB fields."
44+
"description": "Core endpoints for analyzing papers for the 6 essential BugSigDB fields.",
4345
},
44-
{
45-
"name": "System",
46-
"description": "System endpoints for health checks, configuration, and metrics."
47-
}
48-
]
46+
{"name": "System", "description": "System endpoints for health checks, configuration, and metrics."},
47+
],
4948
)
5049

5150
app.add_middleware(
@@ -61,18 +60,15 @@
6160
app.add_middleware(RequestIDMiddleware)
6261

6362
if ENABLE_RATE_LIMITING:
64-
app.add_middleware(
65-
RateLimitMiddleware,
66-
requests_per_minute=RATE_LIMIT_PER_MINUTE,
67-
enabled=ENABLE_RATE_LIMITING
68-
)
63+
app.add_middleware(RateLimitMiddleware, requests_per_minute=RATE_LIMIT_PER_MINUTE, enabled=ENABLE_RATE_LIMITING)
6964
logger.info(f"Rate limiting enabled: {RATE_LIMIT_PER_MINUTE} requests/minute")
7065

7166
app.include_router(bugsigdb_analysis.router)
7267
app.include_router(bugsigdb_analysis_v2.router)
7368
app.include_router(study_analysis.router)
7469
app.include_router(system.router)
7570

71+
7672
@app.get("/")
7773
async def root():
7874
"""API root endpoint."""
@@ -82,14 +78,15 @@ async def root():
8278
"description": "AI-powered Curatable Signature analysis API",
8379
"documentation": "/docs",
8480
"health_check": "/health",
85-
"metrics": "/metrics"
81+
"metrics": "/metrics",
8682
}
8783

8884

8985
@app.get("/health")
9086
async def health_check():
9187
"""Health check endpoint."""
9288
from app.api.routers.system import health_check as system_health_check
89+
9390
return await system_health_check()
9491

9592

@@ -102,41 +99,42 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE
10299
content={
103100
"error": "Validation Error",
104101
"detail": exc.errors(),
105-
"request_id": getattr(request.state, "request_id", None)
106-
}
102+
"request_id": getattr(request.state, "request_id", None),
103+
},
107104
)
108105

109106

110107
@app.exception_handler(Exception)
111108
async def global_exception_handler(request: Request, exc: Exception):
112109
"""Handle unexpected exceptions."""
113110
from app.utils.credential_masking import mask_exception_message, mask_string
114-
111+
115112
# Mask any credentials in exception message and traceback
116113
safe_exc_msg = mask_exception_message(exc)
117114
safe_traceback = mask_string(traceback.format_exc())
118-
115+
119116
logger.error(
120117
f"Unhandled exception: {safe_exc_msg}\n"
121118
f"Traceback: {safe_traceback}\n"
122119
f"Request ID: {getattr(request.state, 'request_id', None)}"
123120
)
124-
121+
125122
if ENVIRONMENT == "production":
126123
detail = "An internal error occurred. Please try again later."
127124
else:
128125
detail = safe_exc_msg
129-
126+
130127
return JSONResponse(
131128
status_code=500,
132129
content={
133130
"error": "Internal Server Error",
134131
"detail": detail,
135-
"request_id": getattr(request.state, "request_id", None)
136-
}
132+
"request_id": getattr(request.state, "request_id", None),
133+
},
137134
)
138135

139136

140137
if __name__ == "__main__":
141138
import uvicorn
139+
142140
uvicorn.run(app, host="0.0.0.0", port=8000)

app/api/middleware/__init__.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
"""
22
API Middleware for production-ready features.
33
"""
4-

app/api/middleware/rate_limit.py

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
"""
22
Rate limiting middleware for API endpoints.
33
"""
4+
5+
import logging
46
import time
5-
from typing import Dict, Optional
67
from collections import defaultdict
7-
from fastapi import Request, HTTPException, status
8+
from typing import Dict, Optional
9+
10+
from fastapi import HTTPException, Request, status
811
from starlette.middleware.base import BaseHTTPMiddleware
9-
import logging
1012

1113
logger = logging.getLogger(__name__)
1214

@@ -17,19 +19,14 @@
1719
class RateLimitMiddleware(BaseHTTPMiddleware):
1820
"""
1921
Rate limiting middleware to prevent abuse.
20-
22+
2123
Uses sliding window algorithm for rate limiting.
2224
"""
23-
24-
def __init__(
25-
self,
26-
app,
27-
requests_per_minute: int = 60,
28-
enabled: bool = True
29-
):
25+
26+
def __init__(self, app, requests_per_minute: int = 60, enabled: bool = True):
3027
"""
3128
Initialize rate limiting middleware.
32-
29+
3330
Args:
3431
app: FastAPI application
3532
requests_per_minute: Maximum requests per minute per IP
@@ -38,79 +35,72 @@ def __init__(
3835
super().__init__(app)
3936
self.requests_per_minute = requests_per_minute
4037
self.enabled = enabled
41-
38+
4239
async def dispatch(self, request: Request, call_next):
4340
"""Process request with rate limiting."""
4441
if not self.enabled:
4542
return await call_next(request)
46-
43+
4744
# Skip rate limiting for health checks and docs
4845
if request.url.path in ["/health", "/docs", "/redoc", "/openapi.json"]:
4946
return await call_next(request)
50-
47+
5148
# Get client IP
5249
client_ip = self._get_client_ip(request)
53-
50+
5451
# Check rate limit
5552
if not self._check_rate_limit(client_ip):
5653
logger.warning(f"Rate limit exceeded for IP: {client_ip}")
5754
raise HTTPException(
5855
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
59-
detail=f"Rate limit exceeded. Maximum {self.requests_per_minute} requests per minute."
56+
detail=f"Rate limit exceeded. Maximum {self.requests_per_minute} requests per minute.",
6057
)
61-
58+
6259
# Process request
6360
response = await call_next(request)
64-
61+
6562
# Add rate limit headers
6663
response.headers["X-RateLimit-Limit"] = str(self.requests_per_minute)
67-
response.headers["X-RateLimit-Remaining"] = str(
68-
self._get_remaining_requests(client_ip)
69-
)
70-
64+
response.headers["X-RateLimit-Remaining"] = str(self._get_remaining_requests(client_ip))
65+
7166
return response
72-
67+
7368
def _get_client_ip(self, request: Request) -> str:
7469
"""Extract client IP address from request."""
7570
# Check for forwarded IP (behind proxy)
7671
forwarded_for = request.headers.get("X-Forwarded-For")
7772
if forwarded_for:
7873
return forwarded_for.split(",")[0].strip()
79-
74+
8075
real_ip = request.headers.get("X-Real-IP")
8176
if real_ip:
8277
return real_ip
83-
78+
8479
return request.client.host if request.client else "unknown"
85-
80+
8681
def _check_rate_limit(self, client_ip: str) -> bool:
8782
"""Check if client IP is within rate limit."""
8883
current_time = time.time()
8984
window_start = current_time - 60 # 1 minute window
90-
85+
9186
# Clean old entries
9287
requests = _rate_limit_store[client_ip]
93-
_rate_limit_store[client_ip] = [
94-
req_time for req_time in requests if req_time > window_start
95-
]
96-
88+
_rate_limit_store[client_ip] = [req_time for req_time in requests if req_time > window_start]
89+
9790
# Check limit
9891
if len(_rate_limit_store[client_ip]) >= self.requests_per_minute:
9992
return False
100-
93+
10194
# Add current request
10295
_rate_limit_store[client_ip].append(current_time)
10396
return True
104-
97+
10598
def _get_remaining_requests(self, client_ip: str) -> int:
10699
"""Get remaining requests for client IP."""
107100
current_time = time.time()
108101
window_start = current_time - 60
109-
102+
110103
requests = _rate_limit_store[client_ip]
111-
valid_requests = [
112-
req_time for req_time in requests if req_time > window_start
113-
]
114-
115-
return max(0, self.requests_per_minute - len(valid_requests))
104+
valid_requests = [req_time for req_time in requests if req_time > window_start]
116105

106+
return max(0, self.requests_per_minute - len(valid_requests))

app/api/middleware/request_id.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"""
22
Request ID middleware for request tracking.
33
"""
4+
5+
import logging
46
import uuid
7+
58
from fastapi import Request
69
from starlette.middleware.base import BaseHTTPMiddleware
710
from starlette.responses import Response
8-
import logging
911

1012
logger = logging.getLogger(__name__)
1113

@@ -14,22 +16,21 @@ class RequestIDMiddleware(BaseHTTPMiddleware):
1416
"""
1517
Middleware to add unique request ID to each request for tracking.
1618
"""
17-
19+
1820
async def dispatch(self, request: Request, call_next):
1921
"""Process request with request ID."""
2022
# Get or create request ID
2123
request_id = request.headers.get("X-Request-ID")
2224
if not request_id:
2325
request_id = str(uuid.uuid4())
24-
26+
2527
# Add to request state
2628
request.state.request_id = request_id
27-
29+
2830
# Process request
2931
response = await call_next(request)
30-
32+
3133
# Add request ID to response headers
3234
response.headers["X-Request-ID"] = request_id
33-
34-
return response
3535

36+
return response

0 commit comments

Comments
 (0)