Skip to content

Commit b66cf4d

Browse files
committed
CI typecheck
1 parent 42cec5b commit b66cf4d

3 files changed

Lines changed: 41 additions & 21 deletions

File tree

frontend_multi_user/src/app.py

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import hashlib
1919
import tempfile
2020
from urllib.parse import quote_plus, urlparse
21-
from typing import ClassVar, Dict, Optional, Tuple, Any
21+
from typing import ClassVar, Dict, Optional, Tuple, Any, cast
2222
from types import SimpleNamespace
2323
from pathlib import Path
2424
from flask import Flask, render_template, Response, request, jsonify, send_file, redirect, url_for, session, abort
@@ -81,6 +81,11 @@
8181
"open_access": "Open access",
8282
}
8383

84+
85+
def _new_model(model_cls: Any, **kwargs: Any) -> Any:
86+
"""Instantiate ORM models through Any to accommodate dynamic Flask-SQLAlchemy typing."""
87+
return cast(Any, model_cls)(**kwargs)
88+
8489
def build_postgres_uri_from_env(env: Dict[str, str]) -> Tuple[str, Dict[str, str]]:
8590
"""Construct a SQLAlchemy URI for Postgres using environment variables."""
8691
host = env.get("PLANEXE_FRONTEND_MULTIUSER_DB_HOST") or env.get("PLANEXE_POSTGRES_HOST") or "database_postgres"
@@ -99,7 +104,7 @@ def __init__(self, user_id: str, is_admin: bool = False):
99104

100105
class MyAdminIndexView(AdminIndexView):
101106
@expose('/')
102-
def index(self):
107+
def index(self) -> Any:
103108
if not current_user.is_authenticated:
104109
return redirect(url_for('login'))
105110
if not current_user.is_admin:
@@ -533,7 +538,7 @@ def _create_tables_with_retry(attempts: int = 5, delay_seconds: float = 2.0) ->
533538
# Setup Flask-Login
534539
self.login_manager = LoginManager()
535540
self.login_manager.init_app(self.app)
536-
self.login_manager.login_view = 'login'
541+
self.login_manager.login_view = cast(Any, 'login')
537542

538543
@self.login_manager.user_loader
539544
def load_user(user_id):
@@ -588,10 +593,11 @@ def _track_flask_app_started(self):
588593
}
589594

590595
with self.app.app_context():
591-
event = EventItem(
596+
event = _new_model(
597+
EventItem,
592598
event_type=EventType.GENERIC_EVENT,
593599
message="Flask app started",
594-
context=event_context
600+
context=event_context,
595601
)
596602
self.db.session.add(event)
597603
self.db.session.commit()
@@ -822,7 +828,8 @@ def _upsert_user_from_oauth(self, provider: str, profile: dict[str, Any]) -> Use
822828
self.db.session.commit()
823829
return user
824830

825-
user = UserAccount(
831+
user = _new_model(
832+
UserAccount,
826833
email=profile.get("email"),
827834
name=profile.get("name") or profile.get("username") or profile.get("login"),
828835
given_name=profile.get("given_name"),
@@ -834,7 +841,8 @@ def _upsert_user_from_oauth(self, provider: str, profile: dict[str, Any]) -> Use
834841
self.db.session.add(user)
835842
self.db.session.commit()
836843

837-
provider_row = UserProvider(
844+
provider_row = _new_model(
845+
UserProvider,
838846
user_id=user.id,
839847
provider=provider,
840848
provider_user_id=provider_user_id,
@@ -866,7 +874,8 @@ def _get_or_create_api_key(self, user: UserAccount) -> str:
866874
raw_key = f"pex_{secrets.token_urlsafe(24)}"
867875
key_hash = hashlib.sha256(f"{api_key_secret}:{raw_key}".encode("utf-8")).hexdigest()
868876
key_prefix = raw_key[:10]
869-
api_key = UserApiKey(
877+
api_key = _new_model(
878+
UserApiKey,
870879
user_id=user.id,
871880
key_hash=key_hash,
872881
key_prefix=key_prefix,
@@ -879,7 +888,8 @@ def _apply_credit_delta(self, user: UserAccount, delta: Decimal, reason: str, so
879888
current_balance = self._to_credit_decimal(user.credits_balance)
880889
next_balance = current_balance + self._to_credit_decimal(delta)
881890
user.credits_balance = max(Decimal("0"), next_balance).quantize(CREDIT_SCALE)
882-
ledger = CreditHistory(
891+
ledger = _new_model(
892+
CreditHistory,
883893
user_id=user.id,
884894
delta=self._to_credit_decimal(delta),
885895
reason=reason,
@@ -916,7 +926,8 @@ def _apply_payment_credits(
916926
if existing:
917927
return "duplicate_payment"
918928
credit_amount = self._to_credit_decimal(credits)
919-
payment = PaymentRecord(
929+
payment = _new_model(
930+
PaymentRecord,
920931
user_id=user.id,
921932
provider=provider,
922933
provider_payment_id=provider_payment_id,
@@ -940,7 +951,8 @@ def _apply_payment_credits(
940951
def _record_event(self, event_type: EventType, message: str, context: Optional[dict[str, Any]] = None) -> None:
941952
"""Best-effort event logging for operational visibility."""
942953
try:
943-
event = EventItem(
954+
event = _new_model(
955+
EventItem,
944956
event_type=event_type,
945957
message=message,
946958
context=context,
@@ -1150,7 +1162,8 @@ def _get_current_user_account(self) -> Optional[UserAccount]:
11501162
admin_pref_id = uuid.uuid5(uuid.NAMESPACE_URL, f"planexe-admin-pref:{self.admin_username}")
11511163
user = self.db.session.get(UserAccount, admin_pref_id)
11521164
if user is None:
1153-
user = UserAccount(
1165+
user = _new_model(
1166+
UserAccount,
11541167
id=admin_pref_id,
11551168
is_admin=True,
11561169
name="Admin",
@@ -2353,7 +2366,10 @@ def stripe_checkout():
23532366
"checkout_payment_status": session_obj.get("payment_status"),
23542367
},
23552368
)
2356-
return redirect(session_obj.url)
2369+
session_url = session_obj.url
2370+
if not isinstance(session_url, str) or not session_url:
2371+
return jsonify({"error": "stripe checkout missing redirect url"}), 502
2372+
return redirect(session_url)
23572373

23582374
@self.app.route('/billing/stripe/webhook', methods=['POST'])
23592375
def stripe_webhook():
@@ -2608,7 +2624,7 @@ def run():
26082624
prompt_param = request_form_or_args.get('prompt', '')
26092625
user_id_param = request_form_or_args.get('user_id', '')
26102626
nonce_param = request_form_or_args.get('nonce', '')
2611-
parameters = {key: value for key, value in request_form_or_args.items()}
2627+
parameters: dict[str, Any] | None = {key: value for key, value in request_form_or_args.items()}
26122628

26132629
# Remove the parameters that have already been extracted from the parameters dictionary
26142630
parameters.pop('prompt', None)
@@ -2673,7 +2689,8 @@ def run():
26732689
if (user.credits_balance or 0) <= 0:
26742690
return jsonify({"error": "No credits available"}), 402
26752691

2676-
task = TaskItem(
2692+
task = _new_model(
2693+
TaskItem,
26772694
state=TaskState.pending,
26782695
prompt=prompt_param,
26792696
progress_percentage=0.0,
@@ -2696,7 +2713,8 @@ def run():
26962713
"parameters": parameters,
26972714
"method": request.method
26982715
}
2699-
event = EventItem(
2716+
event = _new_model(
2717+
EventItem,
27002718
event_type=EventType.TASK_PENDING,
27012719
message=f"Enqueued task via /run endpoint",
27022720
context=event_context
@@ -2718,7 +2736,7 @@ def create_plan():
27182736
request_content_type: str = request.headers.get('Content-Type', '')
27192737

27202738
prompt_param = request.form.get('prompt', '')
2721-
parameters = {key: value for key, value in request.form.items()}
2739+
parameters: dict[str, Any] = {key: value for key, value in request.form.items()}
27222740
parameters.pop('csrf_token', None)
27232741
parameters.pop('prompt', None)
27242742
parameters.pop('user_id', None)
@@ -2763,7 +2781,8 @@ def create_plan():
27632781
if self._to_credit_decimal(user.credits_balance) < Decimal("2"):
27642782
return jsonify({"error": "Insufficient credits (minimum 2 required)"}), 402
27652783

2766-
task = TaskItem(
2784+
task = _new_model(
2785+
TaskItem,
27672786
state=TaskState.pending,
27682787
prompt=prompt_param,
27692788
progress_percentage=0.0,
@@ -2787,7 +2806,8 @@ def create_plan():
27872806
"parameters": parameters,
27882807
"method": request.method
27892808
}
2790-
event = EventItem(
2809+
event = _new_model(
2810+
EventItem,
27912811
event_type=EventType.TASK_PENDING,
27922812
message="Enqueued task via /create_plan endpoint",
27932813
context=event_context

frontend_multi_user/tests/test_plan_failure_trace_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
APP_MODULE_PATH = FRONTEND_SRC / "app.py"
2626
APP_IMPORT_ERROR = None
2727
APP_AVAILABLE = False
28-
MyFlaskApp: type[Any] = object
28+
MyFlaskApp: Any = object
2929
try:
3030
APP_SPEC = importlib.util.spec_from_file_location("frontend_multi_user_app", APP_MODULE_PATH)
3131
if APP_SPEC is None or APP_SPEC.loader is None:

frontend_multi_user/tests/test_plan_telemetry_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
APP_MODULE_PATH = FRONTEND_SRC / "app.py"
2929
APP_IMPORT_ERROR = None
3030
APP_AVAILABLE = False
31-
MyFlaskApp: type[Any] = object
31+
MyFlaskApp: Any = object
3232
TaskState: Any = SimpleNamespace(processing="processing", completed="completed")
3333
frontend_app_module = None
3434
try:

0 commit comments

Comments
 (0)