Quick Start
This guide walks through your first type check with Basilisk. Estimated time: 5 minutes.
Step 1 — Run your first check
Create a Python file, or use the example from the repository. Every problem in it is a genuine violation of the Python typing spec — no configuration needed:
# bad.py
def greet(name: str) -> str:
return "Hello, " + name
greet(42)
count: int = "zero"
def describe(flag: bool) -> str:
if flag:
label = "on"
return label
Run Basilisk:
basilisk check bad.py
Output:
error[calls_argument_type]: Argument `name` of `greet` expects `str` but received an `int` literal
--> bad.py:5:7
|
5 | greet(42)
| ^^
|
= help: Pass a value of type `str` for parameter `name`
= note: Basilisk checks that literal arguments are compatible with declared parameter types
= see: https://www.basilisk-python.dev/errors/calls_argument_type
error[assignment_compatibility]: Type mismatch: `count` is annotated `int` (int) but assigned str
--> bad.py:8:1
|
8 | count: int = "zero"
| ^^^^^
|
= help: Either change the annotation to match the value, or change the value to `int`
= note: Basilisk requires the inferred type to be assignable to the declared type
= see: https://www.basilisk-python.dev/errors/assignment_compatibility
error[names_unbound]: Function `describe` returns `label` but `label` may be unbound on some paths
--> bad.py:14:12
|
14 | return label
| ^^^^^
|
= help: Assign `label` unconditionally before the `return`, or add a default value
= note: Basilisk detects variables that are assigned only inside conditional branches (if/while/try) and may not be defined on every execution path
= see: https://www.basilisk-python.dev/errors/names_unbound
Found 3 diagnostics (3 errors).
Out of the box, Basilisk enables the complete PEP typing-spec rule set, and every violation is an error. Nothing here is house style — this is the Python type system specification, enforced.
Step 2 — Fix the errors
# good.py
def greet(name: str) -> str:
return "Hello, " + name
greet("world")
count: int = 0
def describe(flag: bool) -> str:
return "on" if flag else "off"
basilisk check good.py
All checked. No issues found.
Step 3 — Turn strictness up to full
A clean pass means your code obeys the typing spec — but nothing yet requires annotations. Basilisk's own strictness rules (annotations on every parameter, return type, attribute, and more) are opt-in and silent by default. Enable them as warnings while you migrate — the code still type-checks, and the warnings tell you strictness isn't at full yet:
# pyproject.toml
[tool.basilisk.rules]
"BSK-0001" = "warning" # parameters need a type where none is inferable
"BSK-0002" = "warning" # functions need a return type where none is inferable
Add an unannotated function to good.py:
def process(data):
return data.upper()
warning[BSK-0001]: Missing parameter type annotation for `data`
--> good.py:15:13
|
15 | def process(data):
| ^^^^
|
= help: Add a type annotation: `data: <type>`
= note: Basilisk requires an explicit parameter type wherever it cannot be inferred; a literal default (e.g. `timeout=30`) infers the type and needs no annotation
= see: https://www.basilisk-python.dev/errors/BSK-0001
warning[BSK-0002]: Missing return type annotation for function `process`
--> good.py:15:5
|
15 | def process(data):
| ^^^^^^^^^^^^^
|
= help: Add a return type: `def process(...) -> <type>:`
= note: Basilisk requires an explicit return type wherever it cannot be inferred; literal-only returns (e.g. `return 42`) infer the type and need no annotation
= see: https://www.basilisk-python.dev/errors/BSK-0002
Found 2 diagnostics (0 errors).
Errors are spec violations; warnings are strictness you haven't finished
adopting. When the warnings hit zero, promote the rules to "error" and your
project is at full strictness.
In VS Code, skip the hand-editing: run Basilisk: Open Configuration Editor
from the Command Palette. It shows every rule with its live severity, previews
changes before applying them, and its tag actions turn a whole rule group on
with one written rule-tags line (e.g. "basilisk" = "error"). See the
configuration reference for the full schema.

Step 4 — Check a directory
Basilisk recursively checks every .py file in a directory:
basilisk check src/
To check the current directory:
basilisk check
Step 5 — Add to pyproject.toml
Create a [tool.basilisk] section in your pyproject.toml:
[tool.basilisk]
include = ["src/", "tests/"]
exclude = ["**/migrations/**"]
With a config file present, running basilisk check uses these settings
automatically. Basilisk has no fixed Python-version default; set one only when
you intend to override project/interpreter evidence. Version checks follow the
pinned typing directives
(python/typing@6ef9f77).
Step 6 — Understand a diagnostic
Basilisk uses the same output format as the Rust compiler (rustc). Every diagnostic includes:
error[calls_argument_type]: Argument `name` of `greet` expects `str`…
^^^^^ ^ ← severity + message
--> bad.py:5:7 ← file:line:column
|
5 | greet(42) ← source context
| ^^ ← caret pointing at the issue
|
= help: Pass a value of type `str` for parameter `name` ← actionable fix
= note: Basilisk checks that literal arguments are… ← explanation
= see: https://www.basilisk-python.dev/errors/calls_argument_type ← documentation link
error[rule_code]— severity plus the rule that fired. PEP typing-spec rules use descriptive codes (calls_argument_type); Basilisk's opt-in strictness rules useBSK-codes (BSK-0001)-->— location in your file^^^^— exactly which token caused the diagnostic= help:— the specific change that will fix it= note:— why the rule exists= see:— link to full documentation
The same information is available in your editor — hover any symbol for its inferred type:

Step 7 — Intentional suppressions
When you genuinely need to use Any or suppress a diagnostic, you can — but you must provide a reason:
# This suppression requires a reason comment
result: Any = legacy_sdk_call() # type: ignore[returns_compatibility]
Suppressions without reasons are themselves flagged. This is intentional: if you need to suppress a diagnostic, you should be able to explain why.
Step 8 — Check type coverage
Type coverage is reported live in the editor. Open the Basilisk icon in the activity bar and expand Module Explorer: every package and file carries a coverage bar and percentage — the share of its symbols that are annotated — alongside its error and warning counts, and icons are tinted by coverage so the untyped corners of a project stand out at a glance. Selecting a module opens it.
Coverage aggregates up the tree, so a package shows the combined figure for everything beneath it.
Step 9 — Profile a running script
Basilisk includes an integrated CPU and memory profiler. To try it in VS Code:
- Open a Python file and click Run & Profile CPU (Current File) in the Python Processes panel (Basilisk icon in the activity bar) — it launches the script and profiles it from the first line. To profile an already-running process instead, click Profile CPU on its row in the same panel.
- Watch inline CPU heat annotations appear on hot lines as samples accumulate
- Run Basilisk: Stop Profiling (
Cmd+Shift+P Cmd+Shift+X/Ctrl+Shift+P Ctrl+Shift+X, or click the status-bar counter) — the Basilisk Profiler panel opens with a flame graph and click-to-source hot-function tables
For memory leak hunting, click Run & Track Memory (Current File) in the same panel (memory tracking rides the debugger, so this launches a debug session for you). Snapshots auto-capture at every pause — or take them manually with Basilisk: Take Memory Snapshot — and Basilisk: Compare Memory Snapshots surfaces leaks as diagnostics in the Problems panel.
See the Profiler guide for the full workflow — flame graphs, reference graphs, memory snapshot comparison, VS Code commands, Zed slash commands, Neovim user commands, and platform requirements.
Next steps
- Configuration reference — full
pyproject.tomlschema - Profiler — CPU heatmaps, flamegraphs, and memory leak detection
- Debugging — F5 to debug, breakpoints, stepping, watch expressions
- All rules — every rule explained, PEP and opt-in alike
- Migration guide — migrating from Pyright or mypy