RightTyper: Dynamic Python Type Annotation
- RightTyper is a dynamic type annotation system that infers static type information from executed code by focusing on typical observed behavior.
- It utilizes Python 3.12’s sys.monitoring interface and AST rewriting for precise runtime instrumentation and container sampling.
- By filtering call traces to cover 80% of executions, RightTyper reframes static checking as anomaly detection, enhancing debugging for legacy code.
Searching arXiv for RightTyper and closely related Python type-annotation systems. arXiv search query: "RightTyper Python type annotation TypeWriter Type4Py Typify" RightTyper is a dynamic type annotation system for Python that infers annotations from actual executions and then emits static type information intended to improve type checking on previously untyped or partially typed codebases. Its defining claim is that inferred annotations should describe typical observed behavior rather than all observed behavior indiscriminately, so that a static checker can treat excluded rare behaviors as potential anomalies rather than silently absorbing them into broad unions (Pizzorno et al., 21 Jul 2025). In this formulation, RightTyper is both an annotation generator and an auditing instrument: it produces precise type annotations based on actual program behavior, and it reframes subsequent static checking as anomaly detection rather than mere conformance to an over-approximated dynamic language semantics (Pizzorno et al., 21 Jul 2025).
1. Position in Python’s type-annotation landscape
RightTyper is motivated by the low adoption of Python annotations and by limitations in prior automation strategies. The paper cites a study of nearly 10,000 popular Python repositories finding that only 7% use annotations at all, and among those, only about 8% of function arguments and return types are annotated (Pizzorno et al., 21 Jul 2025). It places prior work into three categories: static inference, AI or LLM-based prediction, and prior dynamic tracing tools. Static inference is described as struggling with reflection, runtime type changes, monkey patching, and dynamic code generation; AI-based approaches are described as inherently unsound and as having difficulty with rare types, user-defined types, and limited vocabularies; prior dynamic systems are described as either too expensive or too inaccurate (Pizzorno et al., 21 Jul 2025).
This positioning differs materially from earlier hybrid and statistical systems. "TypeWriter" combines a learned predictor for Python function argument and return types with search-based validation against a gradual type checker, and reports top-1 F1 of 0.64 for return types and 0.57 for argument types on its internal corpus, with top-5 scores of 0.79 and 0.80, respectively (Pradel et al., 2019). "Type4Py" frames Python type prediction as deep similarity learning over a type-checked dataset and reports an MRR@10 of 77.1%, with 75.8% Top-1 exact match and 79.2% Top-10 exact match on its benchmark (Mir et al., 2021). RightTyper is different in kind: it is not primarily a predictor over a type vocabulary, but a runtime-observation system that attempts to synthesize annotations from executed behavior and then use static type checking to expose unusual cases (Pizzorno et al., 21 Jul 2025).
A common misconception is that a dynamic annotation tool should simply union together everything it sees. RightTyper explicitly rejects that assumption. The paper argues that previously untyped code should not be assumed correct, and that if a buggy edge case returns None once, a tool that merely unions all observations may “explain away” the bug rather than help reveal it (Pizzorno et al., 21 Jul 2025). This suggests a narrower notion of usefulness than exhaustive behavioral summarization.
2. Runtime observation and instrumentation model
RightTyper collects evidence at runtime. Most monitoring uses Python 3.12’s sys.monitoring interface, specifically the events PY_START, PY_RETURN, and PY_YIELD, to observe function entry and arguments, return values, and generator yields (Pizzorno et al., 21 Jul 2025). Coroutines are treated separately because sys.monitoring does not directly expose received coroutine values; for that case, RightTyper intercepts code loading, rewrites the program’s AST, recompiles it, and inserts instrumentation manually (Pizzorno et al., 21 Jul 2025).
The implementation is about 5,000 lines of Python, requires Python 3.12+ to run because of sys.monitoring, and can emit annotations targeting Python 3.9 through 3.13 (Pizzorno et al., 21 Jul 2025). This is a systems-oriented design rather than a purely inferential one. Instrumentation is integral to the method, not an optional auxiliary channel.
Sampling is central to the design. RightTyper samples at two levels: event sampling and container-content sampling (Pizzorno et al., 21 Jul 2025). In event sampling, monitoring is initially enabled broadly so that every executed function is sampled at least once; after processing an event from a code region, monitoring for that region can be disabled, and a self-profiling controller re-enables monitoring when the fraction of execution inside RightTyper’s own code falls below a threshold of 5% by default (Pizzorno et al., 21 Jul 2025). The paper describes this qualitatively rather than as a formal stochastic control policy.
Container typing is also sampled. For standard containers that lack generic metadata, RightTyper samples elements to infer parameter types, with a default limit of 1,000 elements (Pizzorno et al., 21 Jul 2025). Dictionaries receive special treatment: RightTyper can optionally replace the standard dict with a custom implementation that stores elements in a list so that random sampling is statistically correct in time via constant-time list access (Pizzorno et al., 21 Jul 2025). Empty containers are emitted as Never when the target Python version supports it, and iterators are handled through a mix of direct observation, post-iteration updates, and gc-based recovery for built-in iterators, falling back to Iterator[Any] if necessary (Pizzorno et al., 21 Jul 2025).
The system also resolves runtime objects to valid annotation syntax. It constructs a map from type objects to import paths by scanning Python modules for type definitions, preferring names in __all__, non-underscore names, shorter names within a package, and avoiding cross-package aliases that are merely re-exports (Pizzorno et al., 21 Jul 2025). This addresses cases where Python’s runtime naming is not itself a usable annotation, such as function in builtins versus types.FunctionType.
3. Type construction, trace filtering, and annotation synthesis
RightTyper records each function invocation as a call trace containing the types of all arguments and the return value (Pizzorno et al., 21 Jul 2025). The crucial design point is that traces preserve relationships between positions in the same call. This allows the system to infer dependent patterns instead of collapsing everything into independent per-position unions.
After execution, call traces are filtered before annotations are produced. By default, RightTyper keeps only traces that together account for 80% of calls in a given context, discarding the remainder (Pizzorno et al., 21 Jul 2025). The paper describes this as statistical filtering of noise, outliers, rare corner cases, accidentally observed buggy values, and other observations that would otherwise broaden unions. It does not supply a formal anomaly score or robust-statistics estimator beyond this 80% rule (Pizzorno et al., 21 Jul 2025).
The generalization stage then transforms the retained traces into annotations. The paper gives Algorithm 1, Generalize, in prose: traces are transposed by signature position, and each position is rebuilt recursively (Pizzorno et al., 21 Jul 2025). If all observed types at a position are specializations of the same generic , RightTyper recurses into the type arguments and reconstructs ; if the same pattern of variability occurs in multiple positions, it introduces or reuses a type variable; otherwise it forms a union (Pizzorno et al., 21 Jul 2025). This is the basis for inferences such as def add[T: (int, str)](a: T, b: T) -> T or a generated TypeVar in older syntax, and it also works recursively for nested generic structures (Pizzorno et al., 21 Jul 2025).
Several language-specific refinements are applied. For numerics, observed int and float may simplify to float following Python’s numeric tower (Pizzorno et al., 21 Jul 2025). For inherited methods, RightTyper avoids using the runtime subclass of self too literally and instead annotates such positions with the defining class, using Self or type[Self] when supported (Pizzorno et al., 21 Jul 2025). For overridden methods, it inspects parent classes and unions observed parameter types with the parameter types declared by the overridden method; when source annotations are unavailable, it consults typeshed (Pizzorno et al., 21 Jul 2025). The paper also mentions an optional extension for NumPy-like arrays that emits jaxtyping-style shape annotations, such as Float64[ndarray, "2 3"], and can generalize shape variables across traces, although this extension is not quantitatively evaluated (Pizzorno et al., 21 Jul 2025).
Annotation emission is ultimately source-to-source rewriting. The paper describes an end-to-end workflow in which observed executions are converted into annotations, files are rewritten, and the result is then surfaced to developers or passed to downstream static checkers (Pizzorno et al., 21 Jul 2025). The article’s emphasis is on generated function-level types and on their subsequent use by static tools such as mypy and pyright, not on a novel checker of its own (Pizzorno et al., 21 Jul 2025).
4. Type checking as anomaly detection
The conceptual novelty most emphasized in the paper is the recasting of type checking as anomaly detection. Rather than trying to describe all observed behavior, RightTyper intentionally describes the common behavior and excludes sufficiently rare traces from the final annotations (Pizzorno et al., 21 Jul 2025). Once these narrower annotations are written back into the program, a static checker can flag code paths that are inconsistent with the inferred “normal” signature.
The paper’s is_value_ok example captures the logic. A buggy function should return bool, but due to an edge-case bug, input 9 falls through and returns None; a static analyzer that reasons over all paths can infer Optional[bool], after which mypy reports “Success,” masking the latent bug (Pizzorno et al., 21 Jul 2025). RightTyper, because it filters toward typical usage, emits -> bool, after which mypy reports error: Missing return statement [return] (Pizzorno et al., 21 Jul 2025). In this view, the checker is not primarily finding annotation inconsistencies; it is surfacing behavioral anomalies relative to the dominant observed usage pattern.
This design also clarifies what RightTyper does not guarantee. The paper is explicit that RightTyper is a dynamic system whose coverage is bounded by executed tests or workloads (Pizzorno et al., 21 Jul 2025). Its soundness claims are in the sense of reflecting observed runtime behavior, not all possible future runs (Pizzorno et al., 21 Jul 2025). Rare but legitimate behaviors may therefore be filtered out by the 80% call-trace threshold, and the resulting annotations can be intentionally incomplete relative to the full set of valid executions (Pizzorno et al., 21 Jul 2025). A plausible implication is that RightTyper is best understood as a bootstrap-and-audit tool rather than as a complete semantic specification engine.
5. Empirical evaluation and comparative standing
RightTyper’s evaluation emphasizes qualitative diagnostic behavior and runtime overhead rather than corpus-wide precision, recall, or F1. The paper explicitly states that it does not report standard corpus-wide precision/recall/F1 for RightTyper’s annotations (Pizzorno et al., 21 Jul 2025). Instead, it uses litmus tests designed to expose failure modes such as interdependent types, unnamed runtime-only types, inheritance and overriding, and latent edge-case bugs (Pizzorno et al., 21 Jul 2025).
On these litmus tests, RightTyper is reported as the only tool marked clean on all four challenge categories, whereas baselines variously cause runtime errors, trigger spurious type-checker errors, fail to infer arguments or returns, mask existing errors, or potentially mask them (Pizzorno et al., 21 Jul 2025). The compared systems are MonkeyType, PyAnnotate, pytype, QuAC, TypeT5, and RightTyper (Pizzorno et al., 21 Jul 2025). Concrete examples include correct inference of a type variable tying argument and return positions together, recovery of ValuesView[int] for dict.values(), correct use of Self, and exposure of a latent missing-return bug that other approaches effectively normalize away (Pizzorno et al., 21 Jul 2025).
The most substantial quantitative result is overhead. Across the benchmark suite, RightTyper reports maximum overhead 0.9× and mean overhead 0.3×, corresponding to the abstract’s summary of “just 30% performance overhead on average” (Pizzorno et al., 21 Jul 2025). The comparative baselines are much heavier: PyAnnotate reports maximum 7.7× and mean 2.9×, while MonkeyType reports maximum 273× and mean 13× (Pizzorno et al., 21 Jul 2025). The benchmark suite includes real applications such as black formatting the scikit-learn package and a Sudoku solver, as well as test suites of black, rich, and tornado, plus pyperformance benchmarks, all run on Python 3.12.8 on a 10-core 3.7 GHz Core i9, 64 GB RAM, SSD, Linux 6.5.6 (Pizzorno et al., 21 Jul 2025).
In relation to adjacent systems, RightTyper occupies a distinct niche. "TypeWriter" uses a neural predictor plus search-based validation with a gradual type checker and can fully annotate between 14% to 44% of sampled files in its evaluation while ensuring file-local type correctness (Pradel et al., 2019). "Type4Py" emphasizes ranking quality over a large type space and reports 77.1% MRR@10 on its benchmark (Mir et al., 2021). "Typify" is a lightweight usage-driven static analyzer that reports lower latency than learned baselines and often stronger results than static tools like Pyre Infer, while remaining weaker than hybrid systems such as HiTyper in many settings (Aman et al., 6 Apr 2026). These systems differ in evidence source: RightTyper relies on runtime observation; TypeWriter and Type4Py rely on learned priors from annotated corpora; Typify relies on static usage propagation (Pizzorno et al., 21 Jul 2025, Pradel et al., 2019, Mir et al., 2021, Aman et al., 6 Apr 2026).
6. Scope, limitations, and broader significance
RightTyper’s stated limitations follow directly from its dynamic design. It requires Python 3.12+ to run, because it depends on sys.monitoring (Pizzorno et al., 21 Jul 2025). Some instrumentation for coroutines and optional dictionary replacement uses AST rewriting and can therefore be intrusive (Pizzorno et al., 21 Jul 2025). Built-in iterators and unnamed internal types remain difficult, and the system sometimes falls back to protocol or Any-like approximations (Pizzorno et al., 21 Jul 2025). The custom dict replacement is optional because some programs depend on exact built-in behavior (Pizzorno et al., 21 Jul 2025). The paper also notes that actual overhead still exceeds the intended 5% self-profiling target because monitoring is attached to code regions rather than individual invocations, so recursive and re-entrant calls can generate extra unrelated events (Pizzorno et al., 21 Jul 2025).
The deeper limitation is epistemic rather than engineering. Because RightTyper infers from execution, it cannot infer what is not exercised (Pizzorno et al., 21 Jul 2025). Because it intentionally filters rare traces, it may omit legitimate but infrequent behavior (Pizzorno et al., 21 Jul 2025). Because its role is to improve the usefulness of downstream type checking rather than to produce a semantically exhaustive type theory, it should not be interpreted as a complete substitute for manually curated annotations in highly polymorphic, library-style code.
Its broader significance lies in shifting the design objective for type automation. The paper argues that the value of inferred annotations in legacy Python code is not exhausted by coverage or by “passes type checker” metrics; it includes their ability to surface anomalous behaviors that broader inferred types would conceal (Pizzorno et al., 21 Jul 2025). This places RightTyper in a larger trend toward operationalizing typing workflows rather than merely predicting type tokens. "AgenticTyper," for example, addresses repository-scale migration for legacy JavaScript through iterative error correction and behavior-preserving transpilation comparison, rather than only local type prediction (Pohle, 21 Feb 2026). A plausible implication is that the enduring contribution of RightTyper is not only its low-overhead dynamic inference, but its reframing of annotation generation as part of a larger auditing and modernization workflow.
In that sense, RightTyper is best understood as a precise, execution-grounded, low-overhead annotation system for Python that narrows observed behavior into representative function signatures and then uses standard static checkers to expose the deviations. Its practical role is strongest where representative workloads exist, annotations are sparse, and the objective is not simply to make a checker green, but to make unusual or unintended behavior visible (Pizzorno et al., 21 Jul 2025).