fix: make Signal.fire keyword-only to type the dispatch contract - #1784
fix: make Signal.fire keyword-only to type the dispatch contract#1784bluetoothbot wants to merge 9 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1784 +/- ##
=======================================
Coverage 99.81% 99.81%
=======================================
Files 33 33
Lines 3839 3839
Branches 553 553
=======================================
Hits 3832 3832
Misses 5 5
Partials 2 2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Coverage is incomplete. Ci is failing |
Rebase with requested adjustmentsBranch StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
Confirmed, both still open. Coverage: Protocol CI: test plan skipped mypy and pre-commit. Suspect Cleanest fix drops Protocol entirely. Keep Note: last force-push rebased only. Feedback step aborted on session limit. |
|
|
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
034cd22 to
1220891
Compare
Re-reviewed at head Protocol gone. Diff now matches your requested variant: keyword-only mypy ignores at Only remaining nit: retitle. Suggest |
|
|
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
|
|
Rebase with requested adjustmentsBranch Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
|
|
e5be312 to
ae8af76
Compare
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
|
|
| zeroconf: Zeroconf, | ||
| service_type: str, | ||
| name: str, | ||
| state_change: ServiceStateChange, |
There was a problem hiding this comment.
do we need a **kwargs: Any throw away as well for back compat?
There was a problem hiding this comment.
Important
Depends on how much compat you want.
**kwargs: Any throwaway costs little and keeps most of this PR's value: typo protection survives, because nmae= gets absorbed by kwargs while name stays required, so mypy still flags a missing argument. Only loss: extra keys pass through to handlers again — same as pre-change behaviour.
Full back-compat needs defaults too. A caller firing only its own keys still breaks on four required params, and defaults would kill the missing-argument check entirely.
Only in-tree caller: browser.py:743. Adopting **kwargs means rewriting test_signal_fire_rejects_unknown_kwarg (tests/test_services.py:306) to assert forwarding, not rejection.
Which way do you want it?
There was a problem hiding this comment.
Probably just accept **kwargs and throw them away in case someone passes garbage it doesn't break
ae8af76 to
0ba635c
Compare
Rebase with requested adjustmentsBranch Caution Force-push safety check — the rewrite of Commits whose changes are GONE from this PR after the rebase:
Previous PR head: Changes applied
Not changed (and why)
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
PR Review — fix: make Signal.fire keyword-only to type the dispatch contractClean, well-scoped change — blocked only on @bdraco's open back-compat question about a throwaway Strengths: the keyword-only signature matches the sole internal caller (
✅ Resolved since last review (1)Previously-flagged issues verified fixed
🟡 Important
1. Back-compat break on the importable `Signal.fire` surface (@bdraco's open request)
|
Signal._handlers was typed list[Callable[..., None]] and fire accepted arbitrary **kwargs, so a typo at a fire site or a missing parameter at a handler only blew up the moment a real service event dispatched - hours into a run on a quiet network. mypy could not catch dispatch mismatches when the contract shifted. Lock the contract down: define a ServiceStateChangeHandler Protocol describing the (zeroconf, service_type, name, state_change) keyword signature, type Signal._handlers as a list of that Protocol, and make Signal.fire keyword-only with the four named parameters. register_handler / unregister_handler still accept Callable[..., None] for back-compat and cast at the boundary.
0ba635c to
c789ca9
Compare
Rebase with requested adjustmentsBranch Changes applied
StatsActions performed
CI statusCI will be checked asynchronously. Automated by Kōan |
Summary
Signal.fire(**kwargs: Any)forwarded arbitrary kwargs to handlers typedCallable[..., None], so a typo at a fire site or a missing parameter at ahandler only surfaced when a real service event happened to dispatch. mypy
could not catch dispatch mismatches.
fire()is now keyword-only with the four documented parameters(
zeroconf,service_type,name,state_change) and forwards themexplicitly, so the dispatch contract is checked statically.
Closes #1779
Changes
Signal.firekeyword-only with an explicit(*, zeroconf, service_type, name, state_change)signature.**kwargs.positional-arg rejection.
Behaviour notes
Signalis importable from the top-level package (back-compat import insrc/zeroconf/__init__.py, not in__all__). Third-party code thatinstantiated its own
Signaland calledfire()with other kwargs nowgets a
TypeErrorinstead of dispatching.Signalis undocumented; thistightening is the intent of the issue.
_services/__init__.pyis inTO_CYTHONIZEandSignalis acdef class. With Cython 3's defaultannotation_typing,service_type: strand
name: strbecome typed arguments in the compiled wheel, so anon-
strargument raisesTypeErrorthere while pure Python forwards it.The only in-tree caller (
browser.py) always passesstr.Test plan
SKIP_CYTHON=1 poetry run pytest tests/(full suite green).poetry run ruff check/ruff format --checkon the touched files (clean).REQUIRE_CYTHON=1regeneration succeeds for_services/__init__.py.Generated by Kōan