A static scanner that flags unit-confusion risks in Python code: variables named with unit suffixes (distance_km, timeout_ms, weight_kg) that are mixed, mismatched, or ambiguous. If your codebase already encodes units in names — or should after one Mars-climate-orbiter-style bug — run this before your next review. Pure stdlib, zero dependencies, offline.
Confidence: High (≥80%) that mixed-unit operations in suffix-named code are worth surfacing; the checker is heuristic, not a proof.
Mars Climate Orbiter was lost because one team used pound-force seconds and another used newton-seconds. In ordinary Python codebases the same failure mode is everywhere: timeout_ms = 30 passed to a function expecting seconds, distance_km added to distance_mi, a weight_lb compared against weight_kg. Runtime type systems don't help because all of these are plain floats/ints. The convention of encoding the unit in the variable name is cheap and widely used — but nothing checks that the convention is followed consistently. Code review catches it by luck.
The scanner parses your source with ast (no execution, no imports of your code) and applies four checks:
- mixed_units_in_operation — a binary op (
+ - * / // %) whose operands carry different unit suffixes (distance_km + distance_mi). - mixed_units_compare — a comparison mixing units (
if timeout_s < timeout_ms). - unit_mismatch_assignment — a unit-suffixed name assigned from an expression carrying a different unit (
total_kg = weight_lb * 2), including same-base cross-unit assignments. - ambiguous_base — the same base name assigned with different units somewhere in the file (
distance_kmanddistance_miboth defined).
Recognized suffixes (as of 2026-10-12): length (km/mi/m/ft/yd/in), time (ms/s/sec/min/hr/h/us), mass (kg/lb/g/oz/t), data (kb/mb/gb/tb), temperature (c/f/k). Syntax errors and pathological nesting are reported as findings, never crash the run.
# 1. Scan one file (~instant)
python unit_confusion_check.py path/to/module.py
# 2. Scan a whole tree and get machine-readable output (~1 s for ~100 files [estimated])
python unit_confusion_check.py src/ --json
# 3. Run the self-test suite (62 cases, ~10 s [Measured])
python self_test.py # ends with: SUMMARY: PASS 62/62Exit codes: 0 clean, 1 warnings found, 2 input error (path not found).
- dimana (0 stars, observed 2026-10-12) — a dimensional-analysis library: quantities carry units through arithmetic and mismatches raise at runtime. Strictly more powerful, but it requires rewriting every calculation to use its
Dimensionalvalues and adds a dependency. unit-confusion-check is a checker over names you already have: zero code changes, zero runtime cost, works on legacy code. Different tool for a different migration budget. - Pint / bulletproof quantities libraries — same runtime-typed-quantity approach as dimana; excellent for new code, but adoption is a project, not a lint pass. They also cannot flag the naming inconsistency in files that don't use them.
- Plain code review / CI grep for
_km— greps find suffixes but cannot see expression structure (a grep cannot know thata_ms < b_sis a comparison of different units). The AST walk is what makes the mixed-operation checks possible.
If you cannot run Python tooling on the target repo, get 70% of the value with one command:
grep -rnE "[A-Za-z_]_(km|mi|ms|kg|lb|s|sec|min|hr)[^A-Za-z0-9_]" src/ | sortEyeball the output for the same base with different suffixes. What you lose: expression-level mixing checks, comparison checks, and the ambiguous-base rollup.
Priority order: zero false positives on unit-suffixed names > zero dependencies > recall of unit risks > language coverage.
- Name-suffix convention only. We deliberately do not infer units from values or call sites — inference is where false positives come from, and a linter that cries wolf gets disabled.
- Small closed unit table instead of a physics algebra. Multiplying
mbysis not flagged as dimensionally odd; only mixing distinct recognized units in one expression is. - Python-only. The AST approach does not transfer to dynamic-eval languages.
- Rename gaming: teams may drop the suffix (the very information that helps humans) to silence warnings. The ambiguous_base check partially counters this, but a suffix-free codebase is invisible to the tool by design.
- Suffix debt migration: a clean scan can be read as "units are safe", inviting cuts to integration-test coverage of conversions. A quiet scan means no suffix-encoded risks, not no unit bugs.
- Unit table ossification: unrecognized units (
_nmi,_psi) are silently ignored; teams may assume coverage that isn't there. Check the table inunit_confusion_check.pybefore relying on domain-specific suffixes.
Does it execute my code? No. Pure ast parsing; your module is never imported.
Does it convert units for me? No — by design (see Design Trade-offs). It points at the line; you decide which side is wrong.
Why line 0 for ambiguous_base? It's a file-level rollup, not a single line.
When NOT to use this tool? (1) Code that already uses a typed-quantity library (dimana/Pint) — the checks would be noise. (2) Greenfield projects unwilling to adopt the suffix convention — enforce the convention first or the tool has nothing to check. (3) Non-Python languages. (4) As a substitute for testing unit conversions — it finds naming inconsistencies, not wrong conversion factors in unsuffixed code.
- [High ≥80%] Teams that suffix unit-bearing names benefit from consistency checks over those names; the convention is already common in embedded/infra Python.
- [Medium 60-79%] A small closed unit table covers most real usage; exotic units can be added by editing
UNITS(a one-line change). - [Medium 60-79%] Static name-based heuristics produce an acceptable false-positive rate (~0 observed on suffix-disciplined code, based on this repo's own suite; [unverified] on third-party codebases).
- [Low <60%] Warnings alone change behavior. Without a CI gate (
exit 1wired into review), naming conventions tend to drift.
Issues/feedback: hcac4735@agent.qq.com. Version: see CHANGELOG.md. All compatibility claims are as of 2026-10-12 (CPython 3.8+ stdlib only).
Part of the zhengming-toolbox — 55 purpose-built AI-engineering tools across neglected niches.
Related tools: log-redaction-spec · secret-rotation-check · retention-policy-check

