Skip to content

framework hygiene: process-global state, silent fail-open excepts, untyped app.state, undated compat #594

Closed

Description

@antosubash

Grouped framework findings from the Oct 2026 survey:

  1. Process-global mutable state with no reset: mail sender (core/mail.py:80-92), lease backend (core/leases.py:152), Sentry (hosting/sentry.py:166). Two create_app calls in one process (tests, embedding) share them, so the last install wins. Hang them off app.state.sm with module-level accessors, or at least a test-plugin reset.
  2. 14 except Exception sites that return/continue silently or at debug level, some on security/readiness paths: hosting/setup_gate.py:76-80, db/listeners.py:80-85, core/menu.py:127, core/_i18n_translator.py:32, core/invalidation.py:125. Write a fail-open/closed policy (WARNING + exc_info minimum; name each deliberate fail-open) and sweep.
  3. 33 getattr(app.state, "<name>", None) reads (public_routes, auth, migration, tenant_resolver, principal_serializer…): a typo or missing install silently defaults. Typed accessors in core, like get_user_directory.
  4. Undated back-compat (CLAUDE.md forbids): ModuleBase.register_event_handlers(self, bus, app=None) "optional for back-compat" (core/module.py:78-80) and module_meta.py:53 "no compatibility check is performed (legacy modules)". Register with deprecate(remove_in=...) + test_deprecation_expiry.py, or drop.
  5. Route guard monkeypatches route.matches (hosting/_route_guard.py, behind a ty: ignore); Speed up the Python test suites: per-worker Postgres DBs, schema reuse, lazy route resolution #577 added a FastAPI contract test — extend it to pin Starlette's Route.matches signature too.
  6. Lazy imports to dodge cycles between setup_gate, _lifespan and _error_handlers: move run_deferred_startup and the error render helper into leaf modules.

From the Oct 2026 tech-debt survey of main@1c4270ac.

Activity

  1. antosubash commented on Oct 23, 2026

    @antosubash
    OwnerAuthor

    Batch-5 scope: items 1, 2, 4, 5 and 6 are being done now. Item 3 (typed app.state accessors for the ~33 getattr(app.state, ...) reads) is deferred to a follow-up because it is a broad, mostly mechanical change best done on its own.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    tech-debtDebt mapped in the Oct 2026 survey

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions