deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (native)

Gawain enforces type annotations on every Python variable, then checks them with ty or pyrefly

Gawain, a new open-source tool announced on Hacker News, flags missing type annotations on Python variables, parameters, returns and class attributes, then runs ty or pyrefly to verify the types.

Gawain enforces type annotations on every Python variable, then checks them with ty or pyrefly

What Gawain does

Gawain is a new open-source tool from developer matheuzgomes, surfaced on the Hacker News front page, that requires type annotations on Python variables, function parameters, return types and class attributes — and then runs a type checker to confirm those annotations hold up.

According to the project's GitHub README, existing type checkers such as mypy, Pyright, ty and pyrefly simply infer the type of an unannotated variable, while linting rules like ruff's ANN checks and flake8-annotations only demand annotations on functions. Gawain goes further and asks for them on variables and class attributes too. The author says the motivation was blunt: a dislike of dict[str, Any].

Two checks in one run

Gawain works in two phases. First it scans for missing annotations and reports each under a rule code: GW001 for a variable assigned with no annotation, GW002 for an unannotated parameter, GW003 for a function missing a return type, and GW004 for an unannotated class attribute, including a self.x assignment the class body never declares. Type checking is skipped until these presence errors are fixed.

Once every annotation is in place, Gawain runs a type checker on the same files — ty by default, or pyrefly — and prints what it finds, such as a function returning a string where its signature promises an int.

Further rules cover constants lacking Final[T] (GW010), a bare Final with no type (GW011), and Any appearing in an annotation (GW020), plus codes for unreadable files and broken or unused suppressions. Individual lines can be silenced with a comment such as # gawain: ignore[GW001].

Modes, installation and configuration

The default mode is called obvious: a name may skip the annotation when its value has an obvious type and is never reassigned — a literal like count = 0, a container of literals like ids = [1, 2, 3], or a constructor call like user = User("ada"). Strict mode, enabled with mode = "strict" under [tool.gawain], wants an annotation on every assignment. Constants are treated separately, with GW010 flagging any constant not annotated Final[T].

Installation comes with a checker bundled as an extra: uv tool install "gawain[ty]" or pip install "gawain[pyrefly]". The ty extra pins ty 0.0.100; the pyrefly extra pins pyrefly 1.4.1 and does not install ty. A bare pip install gawain provides only the gawain command, which then needs a checker on PATH — or the --no-types flag to run annotation checks alone. The README warns that ty is in beta and its diagnostics may change between versions, pointing affected projects to pyrefly or --no-types.

Gawain exits with 0 when nothing is reported, 1 when it reports an error, and 2 on a usage error or when the type checker cannot run. There is a pre-commit hook, though building it requires Rust 1.97 or newer, and --format json emits machine-readable output for other tooling.

Performance

The README's benchmark, run on the CPython 3.14 standard library — 1,847 files and 974,586 lines — reports a median of 0.46 seconds for Gawain in obvious mode and 0.36 seconds in strict mode across 30 runs. Ruff's ANN rules, which check only parameters and returns, took 0.35 seconds, while flake8-annotations took 11.4 seconds. The author notes this is a single benchmark on one machine, an AMD Ryzen 7 3700X under WSL2, with Gawain run using --no-types. Gawain parses Python with ruff_python_parser, and its checkers come from Astral (ty) and Meta (pyrefly). The name itself, the author admits, simply reflects a fondness for Arthurian tales.

Why it matters

Most Python typing tooling stops at one of two points: checkers that quietly infer types, or linters that annotate functions only. Gawain's bet is that explicit types everywhere — module-level variables and class attributes included — make code easier to read and harder to break, and that Any leaking through a signature is a defect in waiting. By pairing presence checks with a real type checker, and by running at roughly ruff speed, it makes the annotate-everything style cheap enough to adopt in CI. Teams already committed to strict typing may find it fills a genuine gap left by ruff and flake8; developers content to rely on inference are, as the README cheerfully puts it, free to stay away.

  • #python
  • #static-typing
  • #linting
  • #open-source
  • #developer-tools

Related posts