Calling Python from Typhon
When you import from a plain .py, several outcomes are possible:
- The module is typed (PEP 484 / 526 annotations everywhere). The checker accepts the types and treats the import like a Typhon module.
- An authored
.dtystub exists for the module. The stub is the typed surface; the runtime.pyis the implementation. - A compiler-bundled stub exists for the module (
httpx,requeststo start). The library is shaped out of the box — no.venvortyc syncneeded. - The package is installed and introspectable.
tycreads its signatures from the venv and type-checks your calls’ argument types and arity automatically. - The module is untyped. Imports return
Any— values bind silently today, but the type system can’t reason about them. Wrap the boundary inunsafe:or annotate the call site to keep the rest of the code base honest.
For the full model — and how these layers compose — see Layers of third-party type-checking.
Typed Python imports
# typed_lib.py — typed Pythondef add(a: int, b: int) -> int: return a + b
# main.tyfrom typed_lib import add
def f() -> int: return add(1, 2) # ✅ types flow throughThe checker reads the Python type annotations and treats add like a Typhon function.
Stubbed Python imports
# src/stubs/redis.dty — authored stubclass Redis: host: str port: int
impl Redis: def get(self, key: str) -> str? def set(self, key: str, value: str) -> bool
# main.tyimport redis
def f() -> str?: let r: redis.Redis = redis.Redis(host="localhost", port=6379) return r.get("k") # ✅ typed via the stubSee Writing .dty Stubs for the stub format.
Bundled-stub imports (httpx / requests)
Some libraries are typed via typeshed rather than in their own source, so venv introspection can’t recover their signatures. tyc ships curated, embedded .dty stubs for the most common of these — httpx and requests to start — and seeds them before introspection runs:
import httpx
def fetch() -> httpx.Response: let client = httpx.Client(timeout=10.0) # ✅ constructor type-checked return client.get("https://example.com") # ✅ qualified return typeNo .venv or tyc sync is required, and the library’s unintrospectable-dependency warning is suppressed. The bundled class shapes are partial, so members the stub doesn’t enumerate stay lenient (no false attribute_not_found). An authored .dty for the same module always overrides the bundle — write one when you want a stricter surface. See Compiler-bundled stubs.
Introspection-checked imports
When a declared dependency is installed and introspectable, tyc reads its signatures straight from the venv via inspect.signature — recovering parameter and return annotations — and type-checks your calls with no authoring at all:
import typed_dep
def f() -> None: typed_dep.process("not an int") # ❌ tyc::type_mismatch (wants int) typed_dep.process(1, 2, 3) # ❌ wrong arityThis covers both function and constructor calls to fully-typed pure-Python deps, through the same tyc::type_mismatch machinery your own code uses. It degrades to a permissive Unknown on anything it can’t model, so it only ever adds true positives.
Untyped Python imports
import messy_lib
def f() -> int: let raw = messy_lib.fetch() # binds to Any — opaque to the checkerThree fixes, in order of preference:
# Option A: write a .dty stub# (best for long-lived dependencies)
# Option B: annotate explicitlylet raw: dict[str, int] = messy_lib.fetch()
# Option C: wrap in unsafeunsafe: let raw = messy_lib.fetch()let parsed: dict[str, int] = ... # re-assert at the boundaryWhat gets emitted
Typhon’s import lowers to a plain Python import. The imported name’s runtime behaviour is unchanged:
import messy_libimport messy_libThe strictness is compile-time only. At runtime, the import resolves the same way Python always has.
Calling async Python
import some_async_lib
async def f() -> str: let body: str = await some_async_lib.fetch_async("url") return bodyCalling an async def Python function from a Typhon async def works as expected. Sync function calling async without await is a hard error (tyc::missing_await).
Where next
- Writing .dty Stubs — the typed-stub format.
- The Unsafe Boundary — the escape hatch’s mechanics.
- Wrapping an Untyped Library (recipe) — worked patterns.