Repository navigation
Require a destination in subscriber() and publisher() overloads - #3234
Merged
IvanKirpichnikov merged 3 commits intoSep 30, 2026
Merged
Conversation
…rloads Every specific overload gave its destination a `...` default, so a call with no channel, list or stream matched the first one: `broker.subscriber()` type checked as `ChannelSubscriber` and `broker.publisher()` as `StreamPublisher`, while both raise `SetupError` at runtime. pyrefly reports it as inconsistent-overload-default (ag2ai#3202). The destination an overload dispatches on is now required. The catch-all for destinations known only at runtime is split in three, one per destination, so a call naming none matches no overload. Once the destination is required, the concurrent overloads' `max_workers: int = ...` overlaps the plain ones, so `max_workers` is required there too. The FastAPI router's publisher takes `list` and `stream` positionally; a required one cannot follow a defaulted positional, so those overloads make them keyword-only, as RedisRegistrator.publisher already does.
vyhuholl
requested review from
IvanKirpichnikov and
powersemmi
as code owners
September 24, 2026 18:45
IvanKirpichnikov
requested changes
Sep 25, 2026
…ode covers With a union receiver the no-destination case stayed green unless both RedisRegistrator and the FastAPI router regressed: mypy reports the call as long as one member rejects it. Each implementation gets its own line now; RedisRouter shares RedisRegistrator with RedisBroker. The channel-positional runtime case goes: `RedisClusterBroker.subscriber` and the FastAPI router's `publisher` already forward `str | None` destinations that way, so `just mypy` fails without that overload. The list- and stream-only forms have no caller in the library and stay.
7 tasks
IvanKirpichnikov
approved these changes
Sep 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Part of #3202.
Every specific overload of
RedisRegistrator.subscriber()/.publisher()and of the FastAPIRedisRoutergave its destination a...default. A call that names no destination matched the first overload:pyrefly reports this as
inconsistent-overload-defaultatredis/broker/registrator.py:49, 371andredis/fastapi/fastapi.py:391, 724: the implementation defaults the destination toNone, and the overload types don't admit it. The overloads lied about what may be omitted. TheNonedefault itself is live, sincelist=andstream=are passed without achannel.Fix
channelin the channel overloads,listandstreamin theirs.RedisRegistrator's catch-all overload for destinations known only at runtime (str | None) is split in three, one per destination.subscriber()andpublisher()with no destination now match no overload, whilebroker.subscriber(stream=maybe_name)still resolves toLogicSubscriber. The FastAPI router never had a catch-all and doesn't get one.max_workers: int = ...becomesmax_workers: int. Once the destination was required, mypy reported the concurrent list and stream overloads as overlapping the plain ones (overload-overlap), because they too claimedmax_workerscould be left out.publisher()takeslistandstreampositionally, and a required parameter can't follow a defaulted positional one. Its list and stream overloads make them keyword-only, asRedisRegistrator.publisher()already does. Its first overload now carries# type: ignore[override], like the Kafka router's: the abstractStreamRouter.publisher(self)takes no arguments, and no overload accepts that call any more. pyrefly counts this as one newbad-override, the same finding the Kafka and Confluent routers already have.No runtime change. The type checker now rejects calls with no destination, which always raised, and positional
list/streamon the FastAPI router's publisher (router.publisher(None, "name")), which still works at runtime.The other brokers keep their
batch: Literal[True] = ...,max_workers: int = ...andno_confirm: Literal[True] = ...defaults. An earlier overload catches the call that omits them, so there the default never selects the overload.Tests
In
tests/mypy/redis.py:check_destination_is_requiredcallssubscriber()andpublisher()with no destination onRedisBroker,RedisRouterand the FastAPIRedisRouter, under# type: ignore[call-overload]. Withfaststream/reverted, mypy fails it with twounused-ignoreerrors.check_runtime_destination_typepins that astr | Nonechannel, list or stream still resolves toLogicSubscriber/LogicPublisher, so splitting the catch-all dropped nothing.Checked
just mypy(1883 files),just pyrightandjust pyreflyare clean,just linterpasses.pyrefly check faststream: the fourinconsistent-overload-defaultfindings are gone; thebad-overrideabove is the only new one.tests/brokers/redis/test_misconfigure.py,test_fastapi.py,test_router.pyandtest_option_surface.pypass in memory (65 tests).Type of change
Checklist
just lintshows no errors)just mypy,just pyrightandjust pyreflypass