Dispatch-width study: do declared target sets stay narrow?

Corpus and method were registered before any repository was fetched; see the pre-registration. Two protocol deviations are recorded there, including one discarded run.

Hypothesis

H1: in real string-dispatched code, dispatch sets are narrow — median width <= 4, and most sites are singletons. H0 (falsifier): median width > 10, or most dispatch sites wide (>10).

Corpus

Five widely-used Python projects with plugin/registry architectures, named in advance: flask, scrapy, celery, pygments, httpie. 983 non-test source files parsed.

Result 1 — width distribution (430 dispatch tables/registries pooled)

statistictargets
median1
p753
p905
p959
p9935
max602 (pygments lexer registry)

88.8% of dispatch tables have <= 4 targets. 3.0% have > 10.

Per project (median / p90 / max): flask 2 / 3 / 3 · scrapy 1 / 3 / 6 · celery 1 / 5 / 220 · pygments 9 / 50 / 602 · httpie 2 / 5 / 6

H1 is supported; H0 is not met. The distribution is sharply right-skewed: a large majority of dispatch is narrow, with a thin tail of very large registries concentrated in projects whose purpose is a catalogue (pygments' lexers, celery's task/state maps).

Result 2 — how much dispatch is statically knowable at all

Pooled non-method call sites: 25,980 direct, 543 unbounded (runtime-string imports: import_string, load_object, computed getattr). 2.05% of non-method call sites are unbounded in the original source — small in count, but these are exactly the sites where "who calls this?" is unanswerable today, and they sit at the architectural seams (plugin loading, middleware chains, entry points).

Result 3 — ground truth cross-check

httpie's formatter and auth plugin registries (plugins/registry.py, output/processing.py) were migrated by hand to rigid. The analyzer predicted widths of 4 and 3; rigid exactness on the migrated module reports:

call sites: 8 · singleton: 6 · table sites: 2 (widths 4, 3)
exactness: 0.750

Analyzer and language agree on the widths. In the original, the same question ("which formatters can run here?") has no static answer: the registry is populated at import time and iterated dynamically. After migration, rdeps json_formatter answers it exactly.

Honest limits

What this licenses the paper to say

That declared-set width in real dispatch-heavy code is narrow at the median (1) and at p90 (5), with a thin heavy tail; that the tail is concentrated in catalogue-shaped registries where a wide set is honest rather than a modelling failure; and that the exactness metric agrees with hand migration on a real cluster. It does not license claims about whole-system migration cost, non-Python ecosystems, or developer behaviour over time.