Skip to content

fix: preserve function node middleware type inference - #1558

Open
Lpk124 wants to merge 2 commits into
RailtownAI:mainfrom
Lpk124:feature/1538/function-node-typing
Open

fix: preserve function node middleware type inference#1558
Lpk124 wants to merge 2 commits into
RailtownAI:mainfrom
Lpk124:feature/1538/function-node-typing

Conversation

@Lpk124

@Lpk124 Lpk124 commented Sep 10, 2026

Copy link
Copy Markdown

Summary

Fixes #1538.

Retry, Timeout, MaxCalls, and Lock can wrap any node signature, so this keeps their Middleware[Any, Any] parameterization from #1541. The decorator-factory overload still needs to defer inference until it receives the decorated function without losing the signature checks from verifier and custom middleware.

This patch carries that signature constraint separately from the middleware's runtime parameter and return slots. The prebuilt middleware uses an unconstrained carrier; signature-aware middleware keeps its parameter and output constraints. function_node(...) returns a private overloaded callable protocol, which preserves the exact sync or async function signature.

The regression fixture covers empty factories, reusable prebuilt lists, mixed verifier lists, async functions, direct calls, and deliberately invalid verifier signatures. docs_validation.sh now uses --warn-unused-ignores, so widening the factory back to Any makes the negative cases fail CI.

Type of change

  • Bug fix
  • Feature
  • Breaking change
  • Docs
  • Refactor / chore / build / tests

Checklist

  • Lint and format pass (ruff check . and ruff format --check .)
  • Tests added and pass locally
  • Docs updated with a mypy-checked regression fixture
  • No breaking changes

Notes

The first commit is Christian-Sidak's work from #1541 with the original authorship preserved. The follow-up commit replaces the factory-wide Any annotation with the constraint carrier and adds the static coverage.

Local checks:

  • mypy 1.19.1: all 48 files under docs/scripts
  • mypy 2.3.1: docs/scripts/function_node_typing.py
  • fixture with the expected ignores removed: six errors under both mypy versions
  • middleware tests: 104 passed
  • Ruff, dependency ordering, the executable fixture, and git diff --check

Christian-Sidak and others added 2 commits September 10, 2026 11:34
Retry, Timeout, MaxCalls, and Lock subclassed the bare Middleware
generic, which mypy resolved as Middleware[Never, Never] under invariant
ParamSpec generics. Combining any two in a single middleware= list caused
_P to collapse to Never, producing a (*Never, **Never) mismatch against
the decorated function's real signature.

Two changes together fix this:

1. Parameterize the four classes as Middleware[Any, Any] so the list
   element type unifies to Middleware[Any, Any] instead of Never.

2. In the function_node overload for func=None (the parametrized-
   decorator form), use Iterable[Middleware[Any, Any]] instead of
   Iterable[Middleware[_P, _TOutput]] so _P is not constrained by the
   middleware list and is instead inferred from the decorated function.

Adds two regression tests combining multiple prebuilt middleware in one
list, and a docs/scripts example that CI mypy-checks.

Fixes RailtownAI#1538

Signed-off-by: Christian-Sidak <61099993+Christian-Sidak@users.noreply.github.com>
Carry middleware signature constraints separately from runtime generic slots so slot-agnostic middleware defers inference while verifier signatures remain checked.

Add positive and negative mypy coverage for empty, reusable, mixed, async, and direct function node forms.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type hints collapse when you use a list of pre-built middlewares

2 participants