Migration Guide
Basilisk includes tooling to help existing codebases adopt it gradually. A full migration can take weeks on large codebases; the tools below make the process tractable.
From Pyright
Step 1 — Import your configuration
If you have a pyrightconfig.json, Basilisk can read it and produce the equivalent [tool.basilisk] config:
basilisk migrate --from pyright
This reads pyrightconfig.json from the current directory and appends the equivalent settings to your pyproject.toml.
Example input (pyrightconfig.json):
{
"include": ["src"],
"exclude": ["**/migrations/**"],
"typeCheckingMode": "strict",
"pythonVersion": "3.12"
}
Generated output (pyproject.toml):
[tool.basilisk]
python-version = "3.12"
include = ["src/"]
exclude = ["**/migrations/**"]
Step 2 — Run Basilisk
basilisk check src/
If you were already using Pyright with typeCheckingMode = "strict", you will see relatively few new errors. The most common additions:
| Pyright rule | Basilisk equivalent |
|---|---|
reportMissingTypeArgument |
BSK-E0015 |
reportReturnType |
BSK-E0002 |
reportArgumentType |
BSK-E0012 |
reportAttributeAccessIssue |
BSK-E0050 |
reportUndefinedVariable |
BSK-E0018 |
reportOperatorIssue |
BSK-E0014 |
Basilisk adds errors that Pyright does not flag even in strict mode:
- BSK-E0011 — Implicit
Anyin parameter and return position - BSK-E0040 — Mutation of unannotated parameters
- BSK-E0060–E0063 — Implicit type coercions
Step 3 — Handle # type: ignore comments
Pyright uses # type: ignore for inline suppressions. Basilisk requires a different format with a mandatory reason:
# Pyright
x = get_value() # type: ignore[reportArgumentType]
# Basilisk
x = get_value() # basilisk: ignore[BSK-E0012] -- third-party API mismatch, tracked in #456
Basilisk will flag bare # type: ignore comments as BSK-W0090 (unused suppression) since it doesn't recognise them. The migration tool suggests the correct format.
Step 4 — Gradual enforcement with migration mode
For a large codebase, enable migration mode to phase errors in as warnings first:
[tool.basilisk]
python-version = "3.12"
include = ["src/"]
[tool.basilisk.migration]
enabled = true
started = "2025-06-01"
enforce_after = "2025-12-01"
In migration mode, all type errors are reported as warnings. After enforce_after, they become errors again.
Strict mode differences
If you used Pyright with typeCheckingMode = "basic" or "standard", you will see significantly more errors with Basilisk — because those modes allow untyped code. This is expected and is the point. Use per-path overrides with deadlines to phase in enforcement on a directory-by-directory basis.
From mypy
Step 1 — Import your configuration
If you have a mypy.ini or setup.cfg with [mypy] section:
basilisk migrate --from mypy
Example input (mypy.ini):
[mypy]
python_version = 3.12
strict = True
ignore_missing_imports = True
exclude = migrations/
Generated output:
[tool.basilisk]
python-version = "3.12"
exclude = ["**/migrations/**"]
# note: ignore_missing_imports → BSK-E0010 is still active;
# use per-path overrides for specific packages without stubs
Step 2 — The mypy --strict flag is a subset
mypy's --strict enables a specific set of flags. Basilisk enforces all of them and more. When migrating from mypy --strict, expect:
- More errors from BSK-E0011 — mypy permits
Anyin some positions that Basilisk does not - More errors from BSK-E0040/E0041 — Basilisk's immutability rules have no mypy equivalent
- More errors from BSK-E0060–E0063 — mypy does not flag implicit numeric coercions
Step 3 — mypy plugins
mypy plugins (Django, SQLAlchemy, Pydantic) do not work with Basilisk. Basilisk's WASM plugin system is planned for Phase 5. Until then, you may see errors for framework-specific patterns that mypy plugins previously suppressed.
Workaround while waiting for WASM plugins:
[tool.basilisk.per-path-overrides."models/**"]
rules.ignore = ["BSK-E0011"] # Django model fields use Any extensively
Step 4 — Update inline suppressions
mypy uses # type: ignore[error-code]. Basilisk requires # basilisk: ignore[BSK-EXXXX] -- reason.
The migration tool generates a list of every # type: ignore comment in your codebase with the suggested Basilisk equivalent and a reminder to add a reason.
Step 5 — Remove the daemon
mypy's daemon (dmypy) is needed because mypy is slow. Basilisk's incremental computation is built-in. Replace your mypy CI step:
# Old (mypy)
- run: dmypy run -- src/
# New (Basilisk)
- run: basilisk check src/
General migration advice
Start with missing annotations
BSK-E0001 (missing parameter annotations) and BSK-E0002 (missing return annotations) tend to cascade. Fixing them first often resolves downstream errors automatically.
Run with just these rules initially:
basilisk check --only E0001,E0002 src/
Use per-path overrides for legacy directories
Don't try to type everything at once. Use per-path overrides with a deadline:
[tool.basilisk.per-path-overrides."legacy/**"]
strict = false
deadline = "2026-06-01"
[tool.basilisk.per-path-overrides."new_modules/**"]
# Full strictness immediately for new code
Track progress with basilisk stats
basilisk stats src/
Output:
Type coverage report — src/
Files: 142 total, 98 fully typed, 44 partially typed
Functions: 1,847 total, 1,203 typed (65%)
Classes: 318 total, 241 typed (76%)
Top offenders (most errors):
src/legacy/data_pipeline.py — 47 errors
src/utils/converters.py — 23 errors
src/models/legacy_models.py — 19 errors
Use this report weekly to track migration progress.
Prioritise the critical path
In practice, errors in the most-imported modules are the highest priority because they can cause type errors in callers too. Fix the deepest shared utilities first.