Coverage metrics
Python type annotations are optional: unannotated code runs fine. As projects grow, missing annotations make it harder for type checkers to catch bugs, for IDEs to give accurate completions, and for contributors to read the code.
typestats measures the type-coverage of Python packages: the percentage of public symbols with type annotations.
Typable
Each annotation slot is counted individually: function parameters, return types, variable annotations, class attributes, and method signatures each contribute one slot. Functions and classes are not counted as single items.
x: int = 0
z: Any = 2
def greet(name: str, greeting: Any) -> str: ...
class Config:
debug: bool
timeout: Any
retries = default_retries()
def __init__(self, name: str) -> None:
self.name = name
def validate(self) -> bool: ...
| Symbol | Type | typed | any | typable | |
|---|---|---|---|---|---|
attr |
x |
int |
1 | 0 | 1 |
attr |
z |
Any |
1 | 1 | 1 |
func |
greet |
||||
↳ param |
name |
str |
1 | 0 | 1 |
↳ param |
greeting |
Any |
1 | 1 | 1 |
↳ return |
str |
1 | 0 | 1 | |
class |
Config |
||||
↳ attr |
debug |
bool |
1 | 0 | 1 |
↳ attr |
timeout |
Any |
1 | 1 | 1 |
↳ attr |
retries |
0 | 0 | 1 | |
↳ meth |
__init__ |
||||
↳↳ param |
name |
str |
1 | 0 | 1 |
↳↳ return |
None |
1 | 0 | 1 | |
↳ attr |
name |
0 | 0 | 1 | |
↳ meth |
validate |
||||
↳↳ return |
bool |
1 | 0 | 1 | |
| Total | 10 | 3 | 12 |
Note that self is excluded because type-checkers do not require it to be annotated.
Unlike mypy and pyright, typestats does not treat types from the stdlib or unknown
third-party packages as Any. If a parameter is annotated x: ArrayLike, it counts
as typed regardless of whether ArrayLike can be resolved.
Coverage
Whether a symbol is annotated is only half the story: Any satisfies the type
checker syntactically, yet provides zero type safety.
That is why typestats reports two coverage metrics.
- Coverage:
typed / typable - Coverage (strict):
(typed - any) / typable
Non-strict coverage counts Any as typed: it shows how much of the API is annotated at
all. Strict coverage excludes Any, counting only annotations a type checker can use.
Since Any provides no type safety, typestats provides this additional strict coverage metric.
From the example above:
- Coverage: 10 / 12 = 83%
- Coverage (strict): (10 - 3) / 12 = 58%
The 25-point gap (3 / 12) is the Any share of the annotations.