"""Read-only Pyramid endpoint probe: python -m app.probe_pyramids. Uses the project's configured account and WqClient without changing stored data. A standalone process authenticates separately; it cannot inherit a running server's in-memory cookies. Authentication responses and secrets are never printed. """ import asyncio import json from .config import Settings from .db import create_database from .models import Account from .security import cipher from .worldquant import WqClient, WqError PATHS = ( "/users/self/activities/pyramid-alphas", "/users/self/pyramid/alphas", "/activities/pyramid-alphas", "/pyramid/alphas", ) async def probe(client): """Probe fixed same-origin GET paths using an authenticated WqClient. Returns status and successful JSON for each path; stops on session expiry. Transport failures propagate to the caller without exposing request details. """ if not client.authenticated: raise WqError("请先连接 WorldQuant", "disconnected") results = [] for path in PATHS: response = await client.client.get(path) result = {"path": path, "status": response.status_code} if response.status_code == 200: try: result["data"] = response.json() except ValueError: result["error"] = "invalid_json" results.append(result) if response.status_code in (401, 429): break return results async def main(): settings = Settings() client = WqClient(settings) engine = None try: if settings.wq_email: email, password = settings.wq_email, settings.wq_password.get_secret_value() else: engine, sessions = create_database(settings.database_url) async with sessions() as db: account = await db.get(Account, 1) if not account or not account.email or not account.password_encrypted: raise WqError("未配置平台凭据", "disconnected") email = account.email password = cipher(settings).decrypt(account.password_encrypted.encode()).decode() await client.authenticate(email, password) print(json.dumps(await probe(client), ensure_ascii=False, indent=2)) except WqError as exc: print(json.dumps({"error": exc.code}, ensure_ascii=False)) return 1 except Exception as exc: # Connection/config errors can contain credentials; print only the type. print(json.dumps({"error": type(exc).__name__})) return 1 finally: await client.close() if engine is not None: await engine.dispose() return 0 if __name__ == "__main__": raise SystemExit(asyncio.run(main()))