Skip to content

Critical async-state runtime invariants are guarded by assert and disappear under python -O #49

@nficano

Description

@nficano

Several hot paths use assert ... is not None to narrow client._transport, client.auto_ack, runtime.credential_provisioner, and runtime.revocation_log before dereferencing them. The pattern appears at src/arcp/_client/client.py:151, src/arcp/_client/client.py:188, src/arcp/_client/ops.py:55, src/arcp/_client/ops.py:87, src/arcp/_client/ops.py:108, src/arcp/_client/ops.py:134, src/arcp/_client/ops.py:171, src/arcp/_client/ops.py:186, src/arcp/_client/dispatch.py:69, src/arcp/_runtime/_handlers.py:193, and src/arcp/_runtime/_job_runner.py:157. The runtime is shipped as a library, so an embedder running python -O or PYTHONOPTIMIZE=1 strips every one of those checks: the code then attempts to call .send, .bind, or .issue on None and raises AttributeError: 'NoneType' object has no attribute ... in the middle of a session instead of the intended InternalError or early-fail behavior. Pyright also reads these asserts as type narrowing, so removing them would surface real None-handling holes that are presently invisible. This is a Python-specific footgun called out in PEP 8 and the assert documentation explicitly: assert is for invariants the program may not need at runtime, not for control flow.

Fix prompt: Replace every assert ... is not None that narrows a load-bearing attribute in src/arcp/_client/ and src/arcp/_runtime/ with an explicit if x is None: raise InternalError("transport not connected") (or the appropriate ARCPError subclass). Keep assert only for genuine developer-time invariants that are safe to strip. Add a regression test that imports the package under python -O and exercises ARCPClient.submit against a disconnected client to confirm the explicit error path fires instead of an AttributeError. After the change, run uv run pyright and resolve any newly surfaced None-related diagnostics rather than re-adding the asserts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingseverity:mediumMedium severity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions