---
title: 'RightTyper: Dynamic Python Type Annotation'
url: https://www.emergentmind.com/topics/righttyper
type: topic
---

# RightTyper: Dynamic Python Type Annotation

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 [2507.16051]. 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 [2507.16051].

## 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 [2507.16051]. 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 [2507.16051].

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 [1912.03768]. "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 [2101.04470]. 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 [2507.16051].

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 [2507.16051]. 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 [2507.16051]. 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 [2507.16051].

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** [2507.16051]. 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 [2507.16051]. 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** [2507.16051]. 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** [2507.16051]. 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 **\( \mathcal{O}(1) \)** time via constant-time list access [2507.16051]. 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 [2507.16051].

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 [2507.16051]. 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 [2507.16051]. 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 [2507.16051]. 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 [2507.16051].

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 [2507.16051]. If all observed types at a position are specializations of the same generic \(G\), RightTyper recurses into the type arguments and reconstructs \(G[\ldots]\); if the same pattern of variability occurs in multiple positions, it introduces or reuses a type variable; otherwise it forms a union [2507.16051]. 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 [2507.16051].

Several language-specific refinements are applied. For numerics, observed `int` and `float` may simplify to `float` following Python’s numeric tower [2507.16051]. 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 [2507.16051]. 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** [2507.16051]. 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 [2507.16051].

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 [2507.16051]. 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 [2507.16051].

## 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 [2507.16051]. 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 [2507.16051]. RightTyper, because it filters toward typical usage, emits `-> bool`, after which mypy reports `error: Missing return statement  [return]` [2507.16051]. 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 [2507.16051]. Its soundness claims are in the sense of reflecting **observed runtime behavior**, not all possible future runs [2507.16051]. 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 [2507.16051]. 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 [2507.16051]. 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 [2507.16051].

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 [2507.16051]. The compared systems are **MonkeyType**, **PyAnnotate**, **pytype**, **QuAC**, **TypeT5**, and RightTyper [2507.16051]. 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 [2507.16051].

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**” [2507.16051]. The comparative baselines are much heavier: **PyAnnotate** reports **maximum 7.7×** and **mean 2.9×**, while **MonkeyType** reports **maximum 273×** and **mean 13×** [2507.16051]. 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** [2507.16051].

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 [1912.03768]. "Type4Py" emphasizes ranking quality over a large type space and reports **77.1% MRR@10** on its benchmark [2101.04470]. "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 [2604.05067]. 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 [2507.16051][1912.03768][2101.04470][2604.05067].

## 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` [2507.16051]. Some instrumentation for coroutines and optional dictionary replacement uses AST rewriting and can therefore be intrusive [2507.16051]. Built-in iterators and unnamed internal types remain difficult, and the system sometimes falls back to protocol or `Any`-like approximations [2507.16051]. The custom `dict` replacement is optional because some programs depend on exact built-in behavior [2507.16051]. 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 [2507.16051].

The deeper limitation is epistemic rather than engineering. Because RightTyper infers from execution, it cannot infer what is not exercised [2507.16051]. Because it intentionally filters rare traces, it may omit legitimate but infrequent behavior [2507.16051]. 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 [2507.16051]. 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 [2602.21251]. 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 [2507.16051].

Source: https://www.emergentmind.com/topics/righttyper