We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Interfaces with Protocols and ABCs step 22 of 26
Write a Minimal Stub for an Untyped Package
Your code calls fastcsv, a package that ships no type hints and has no
stub package. Here is consumer.py:
from fastcsv import read_rows
def load(path: str) -> list[list[str]]:
return read_rows(path)
To mypy, every value from fastcsv is Any, a type that turns checking
off. So strict mode reports:
Returning Any from function declared to return "list[list[str]]" [no-any-return]
The fix is a stub file, a .pyi file that holds only type information
for a module, with ... in place of function bodies. Save this as
stubs/fastcsv.pyi:
def read_rows(path: str) -> list[list[str]]: ...
Then point mypy at the folder:
MYPYPATH=stubs python -m mypy --strict consumer.py
mypy now reads the stub instead of the package, and knows only the names
the stub declares. So a stub needs every name your code uses, and each
signature should match what the package does at runtime. Before you write one,
look for a published stub package, often named types- plus the package
name.
What to write
Write a tool that drafts a stub with exactly the names a consumer file uses. You supply the signatures. The tool picks the names and formats the lines.
def used_names(consumer_source: str, module: str) -> set[str]: ...
def solve(consumer_source: str, module: str,
signatures: dict[str, list[str]]) -> list[str]: ...
module is an absolute module name such as fastcsv or vendor.fastcsv.
Parse the source with ast and never run it. Let a SyntaxError propagate.
Which names to collect
- From
from fastcsv import a, b as c, collectaandb, used or not. Skip*and relative imports. - After
import fastcsv as fc, collectxfrom everyfc.x. Afterimport vendor.fastcsvwith noas, collectxfrom everyvendor.fastcsv.x. - Collect only the first attribute after the module, so
fc.Reader.parsegivesReader. Reads, assignments anddelall count. - Match the module name exactly.
import vendororimport vendor.fastcsv.subdoes not matchvendor.fastcsv. - Find every import first, then look for attributes. Search the whole file,
including nested functions and code that never runs. Ignore scopes, so a
local variable named
fcstill matches. - Return each name once.
What solve returns
For each collected name, in sorted order:
signatures[name] |
Lines |
|---|---|
| One signature | def NAME, the signature, then : ... |
| Two or more | @overload and then the def line, for each signature in the given order |
| Missing or empty | NAME: Incomplete |
A signature is everything after the name, such as
(path: str) -> list[list[str]]. Copy it exactly. Keep repeated signatures,
and add no imports for the types inside them.
Incomplete, from _typeshed, is an alias for Any that marks a type you
still have to work out. _typeshed exists only for type checkers, so import
it only in a .pyi file.
Start with the header imports the body needs.
from _typeshed import Incomplete comes first, then
from typing import overload. Follow them with one empty string, then the
body. With no header, start with the body. With no collected names, return
[]. Leave signatures unchanged.
Example
import fastcsv as fc
from fastcsv import Dialect
def go(path: str) -> None:
fc.write(path, [])
fc.sniff(path)
With these signatures:
signatures = {
"write": [
"(sink: str, rows: list[list[str]]) -> None",
"(sink: int, rows: list[list[str]]) -> int",
],
"sniff": ["(sample: str) -> str"],
}
solve returns:
[
"from _typeshed import Incomplete",
"from typing import overload",
"",
"Dialect: Incomplete",
"def sniff(sample: str) -> str: ...",
"@overload",
"def write(sink: str, rows: list[list[str]]) -> None: ...",
"@overload",
"def write(sink: int, rows: list[list[str]]) -> int: ...",
]
mypy also ships stubgen, which drafts a stub from a package, and
stubtest, which compares a stub with the installed package. See
mypy's guide to missing imports.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.