[App Lifecycle]
Flask application setup: MAX_CONTENT_LENGTH, CORS policy, blueprints, error handlers, and process entry.
App Lifecycle
Abstract
backend/app.py constructs a single global Flask app, applies security-minded defaults (body size, CORS allowlist), registers free and Pro routes, and exposes a module __main__ for local development. There is no multi-app factory pattern today — import app for WSGI (gunicorn) or run the module for the built-in server.
Conceptual model
Interface surface
Process entry
make run
# equivalent
python -m backend.app
Environment:
| Variable | Default | Role |
|---|---|---|
HOST | 127.0.0.1 | Bind address (localhost-first for dev) |
PORT | 5002 | Listen port |
ALLOWED_ORIGINS | https://pynescript.ai, https://app.pynescript.ai, localhost regex | CORS origins (comma-separated; regex allowed) |
API_KEY_STORE | /root/pynescript/data/api_keys.json | JSON key store path (file-backed default) |
ADMIN_TOKEN | unset | Documented for admin create_key (see auth) |
debug=False always on the dev runner.
Health
GET / → JSON:
{
"status": "healthy",
"service": "pynescript-pro-api",
"version": "1.0.0",
"timestamp": 0,
"endpoints": { "…": "…" }
}
Hard limits
MAX_CONTENT_LENGTH = 5 * 1024 * 1024— reject oversized bodies before JSON parse fills memory (audit 2026-07-05 / S1).- CORS methods:
GET,POSTonly. - Allow headers:
Content-Type,Authorization,X-Admin-Token. supports_credentials=False.
Localhost regex used in the default origin list:
^https?://(?:localhost|127\.0\.0\.1)(?::\d+)?$
Same-origin / no Origin header (curl, server-to-server) remains usable under flask-cors behavior.
Blueprints
app.register_blueprint(preview_bp) # url_prefix=/preview
app.register_blueprint(backtest_bp) # url_prefix=/backtest
Defined in backend/api/preview.py.
Error handlers
| Code | Body code | Message |
|---|---|---|
| 404 | NOT_FOUND | Endpoint {path} not found. |
| 500 | INTERNAL_ERROR | Internal server error. |
Both return JSON with status: "error".
Internals
| Path | Role |
|---|---|
backend/app.py | App object, free routes, auth routes |
backend/api/preview.py | Pro blueprints |
backend/middleware/schemas.py | Request validation for /run* and auth |
backend/requirements.txt | Flask, flask-cors, numpy, matplotlib, … |
Recommended production: gunicorn/uvicorn-class WSGI with multiple workers; for multi-worker key consistency prefer SQLite/Redis stores over the default in-memory/file singleton assumptions — see auth.
Invariants and edge cases
- Import side effects: creating
appconfigures CORS immediately from env. - Body too large → Flask 413 before route logic (not custom JSON).
- Unknown fields on schema-validated routes →
UNKNOWN_FIELDS400 (/run, auth); preview routes currently use looserget_json()(schemas exist but are not always applied in handlers). - Dev bind default is loopback — set
HOST=0.0.0.0only when intentional.
Worked example — smoke test
curl -s http://127.0.0.1:5002/ | jq .status
# "healthy"
Failure modes
| Symptom | Cause |
|---|---|
| Browser CORS errors | Origin not in ALLOWED_ORIGINS |
| 413 on large OHLCV | Exceeded 5 MB — downsample or paginate |
| Import errors for matplotlib | Missing backend deps — pip install -r backend/requirements.txt |