Latest Results
refactor: modernize typing and defaults across core API (#1299)
* refactor: modernize typing to PEP 604 style across core API
- Replace Union[X, Y] with X | Y
- Replace Optional[X] with X | None
- Replace List[X], Dict[K, V], Tuple[X] with lowercase builtins
- Import Callable from collections.abc instead of typing
- Fix mutable default arguments (Config(), DependencyMap(), list defaults)
- Improve OpenAPI union schema generation using get_origin/get_args
- Make TemplateInterface.__init__ accept *args/**kwargs
- Clean up unused typing imports
Co-authored-by: Sachin-Bhat <sachin.bhat@users.noreply.github.com>
Made-with: Cursor
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* fix: lint and Generator typing failures
- robyn/responses.py: Generator[str | None | None] is malformed (typing.Generator
needs 3 args yield/send/return); restore Generator[str, None, None] | AsyncGenerator[str, None].
- robyn/processpool.py: process_pool: List = [] → list = [] (List was unimported).
- integration_tests/base_routes.py: TestTypedBody.items: List[str] → list[str].
- Drop unused typing imports flagged by ruff F401: Optional in robyn/ai.py,
Dict/List in robyn/mcp.py, Protocol/TypeAlias in robyn/openapi.py, the
TYPE_CHECKING-gated SubRouter import in robyn/router.py.
- ruff-format pass: insert PEP 8 blank lines between top-level defs across
ai/authentication/cli/logger/mcp/processpool/reloader/responses/router/
types/__init__.py.
Fixes ubuntu/macos/windows tests, codspeed-benchmarks, lint, and pre-commit.ci.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: restore mutable defaults for Config/DependencyMap and handle PEP 604 unions in param coercion
Two test regressions exposed by the typing modernization:
1. test_subrouter_global_dependency_injection expected app's later
inject_global(GLOBAL_DEPENDENCY="GLOBAL DEPENDENCY") to override the
subrouter's earlier inject_global(...="GLOBAL DEPENDENCY OVERRIDE").
That worked on main only because BaseRobyn(... config=Config(),
dependencies=DependencyMap()) used mutable defaults — every Robyn /
SubRouter constructed without explicit args shared the same instances,
so app.inject_global mutated the same dict the subrouter held. The PR
moved both to None + lazy-init in __init__, breaking the sharing.
Restored the mutable defaults to match main's semantics. SubRouter
signature also restored to match main (Config()/OpenAPI() defaults).
2. test_easy_access_optional_present[sync|async] returned 500 because
robyn/_param_utils.py:unwrap_optional only recognized typing.Union
via .__origin__. PEP 604 `int | None` is types.UnionType and has no
__origin__ attribute, so unwrap_optional returned the raw union and
coerce_value tried (int | None)("30"), raising. Replaced
getattr(annotation, "__origin__", None) with typing.get_origin and
accept both Union and types.UnionType. Same fix applied to
is_list_type for symmetry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Sanskar Jethi <sansyrox@gmail.com>
Co-authored-by: Sachin-Bhat <sachin.bhat@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> perf: skip Python Response wrapping for bare dict/list/str/bytes returns (#1384)
* perf: skip Python Response wrapping for bare dict/list/str/bytes returns
Handlers that return a dict/list/str/bytes no longer allocate a Python-side
`Response(...)` or fresh `Headers({...})` per request. The router now lets
those values flow through to the Rust executor, which:
- downcasts `#[pyclass] Response` directly (0 getattr calls) when the user
did return one,
- serializes bare dicts/lists via a cached `orjson.dumps`,
- wraps bare str/bytes with prebuilt content-type headers,
- falls back to the existing `FromPyObject<Response>` chain for subclasses.
Also skips the per-request `contextvars.Context()` allocation when no
middleware is registered — the #1380 cross-phase context is only needed
when `before_request`/`after_request` hooks can mutate `ContextVar`s.
Local micro-benchmark on `/json` (workers=1, processes=1, single-threaded
urllib client): 4013 rps -> 5728 rps (+43%).
All 339 integration tests and 23 unit tests pass (6 pre-existing
`test_openapi_schema.py` failures unrelated to this change).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* review: address CodeRabbit feedback
- cargo fmt: wrap the `get_description_from_pyobject` call that rustfmt
flagged on the PyResponse fast path.
- `downcast_exact::<PyResponse>()` so user Response subclasses with
overridden properties fall through to the getattr slow path, preserving
their custom read semantics.
- `downcast` (not `downcast_exact`) for PyDict/PyList/PyString/PyBytes so
primitive subclasses match the Python router's `isinstance(...)` gate
instead of falling through to the FromPyObject chain (which would fail
with TypeError).
- Revert the conditional per-request `contextvars.Context()`: with it
skipped, sync handlers calling `ContextVar.set(...)` would leak values
into the next request on the same worker thread, weakening the #1380
isolation guarantee. The J1-J4 fast paths alone still deliver the win
(~+43% locally on /json with workers=1, processes=1).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Latest Branches
0%
pre-commit-ci-update-config 0%
Sachin-Bhat:refactor/pep_604_compliance 0%
© 2026 CodSpeed Technology