Conversion Rules¶
The converter applies deterministic, source-grounded mappings from each source framework to Lilya.
Rule Model¶
- Rules are adapter-specific and explicitly registered.
- Each rule has a stable ID (
map rules). - Applied rules are reported per conversion (
map applied). - Unsupported or partial mappings emit diagnostics with file and line context.
FastAPI Rules¶
Imports and constructors¶
- FastAPI app/router imports map to Lilya imports.
- Response and middleware imports map only when Lilya has direct equivalents.
- Unsupported constructor kwargs are removed and reported.
Routing and dependencies¶
include_router(...)->include(path=..., app=...).api_route(...)andtrace(...)normalize to Lilya route methods.- Router
prefix=is merged into route paths where deterministic. Depends(...)andAnnotated[..., Depends(...)]are normalized to Lilya dependency maps.
Middleware and handlers¶
- FastAPI exception-handler decorators become
add_exception_handler(...)calls. - Unsupported decorator middleware patterns are removed with diagnostics.
Flask Rules¶
- Flask/Blueprint imports map to Lilya app/router imports.
- Blueprint
url_prefixis merged into route decorators where deterministic. register_blueprint(...)->include(path=..., app=...)with non-empty path normalization.- Unsupported route and constructor kwargs are removed with diagnostics.
Django Rules¶
django.urls.path(...)andre_path(...)-> LilyaPath(...).django.urls.include(...)-> LilyaInclude(path=..., app=...).urlpatternsmodules are materialized asapp = Lilya(routes=urlpatterns).- Converter syntax (
<int:id>) is normalized to Lilya syntax ({id:int}). - Project path mapping:
management/commands/*->directives/operations/*.
Litestar Rules¶
- Litestar imports map to Lilya app/routing imports.
- Module-level HTTP decorators (
@get,@post, etc.) are converted to explicitPath(...)routes. route_handlersinLitestar(...)andRouter(...)are normalized to Lilyaroutes.Router(path=...)is materialized viaInclude(path=..., app=router)at app level.
Starlette Rules¶
- Starlette imports map to Lilya import aliases:
Starlette->Lilya,Route->Path,Mount->Include,WebSocketRoute->WebSocketPath.Route(...),Mount(...), andWebSocketRoute(...)call signatures are normalized.mount(...)calls normalize to Lilya include semantics.add_route(route=...)calls normalize toadd_route(handler=...).
Path Normalization¶
For frameworks with route/include path values, converter output enforces Lilya path expectations:
- path values are never empty,
- path values are normalized to start with
/where deterministically possible.
Worked Examples¶
FastAPI dependencies¶
from typing import Annotated
from fastapi import Depends, FastAPI
def global_dep() -> str:
return "global"
app = FastAPI(dependencies=[Depends(global_dep)])
@app.get("/items")
async def items(
user: str = Depends(global_dep),
value: Annotated[int, Depends(global_dep)] = 1,
):
return {"user": user, "value": value}
from lilya.apps import Lilya as FastAPI
from lilya.dependencies import Provide, Provides
def global_dep() -> str:
return "global"
app = FastAPI(dependencies={"_global_dep": Provide(global_dep)})
@app.get(
"/items",
dependencies={"_global_dep": Provide(global_dep), "user": Provide(global_dep), "value": Provide(global_dep)},
)
async def items(user: str = Provides(), value: int = 1, *, _global_dep=Provides()):
return {"user": user, "value": value}
FastAPI complex conversion¶
from typing import Annotated
from fastapi import APIRouter, Depends, FastAPI
def app_dep() -> str:
return "app"
def include_dep() -> str:
return "include"
def router_dep() -> str:
return "router"
def route_dep() -> str:
return "route"
app = FastAPI(openapi_url="/openapi.json", dependencies=[Depends(app_dep)])
router = APIRouter(prefix="/api", dependencies=[Depends(router_dep)], tags=["ignored"])
@router.api_route(
"/items/{item_id}",
methods=["GET", "POST"],
dependencies=[Depends(route_dep)],
response_model=dict,
)
async def item(
item_id: int,
token: Annotated[str, Depends(route_dep)],
extra: str = Depends(router_dep),
):
return {"id": item_id, "token": token, "extra": extra}
app.include_router(router, prefix="/v1", dependencies=[Depends(include_dep)], tags=["ignored"])
from lilya.apps import Lilya as FastAPI
from lilya.dependencies import Provide, Provides
from lilya.routing import Router as APIRouter
from typing import Annotated
def app_dep() -> str:
return "app"
def include_dep() -> str:
return "include"
def router_dep() -> str:
return "router"
def route_dep() -> str:
return "route"
app = FastAPI(dependencies={"_app_dep": Provide(app_dep)}, enable_openapi=True)
router = APIRouter()
@router.route(
"/api/items/{item_id}",
methods=["GET", "POST"],
dependencies={
"_route_dep": Provide(route_dep),
"_router_dep": Provide(router_dep),
"token": Provide(route_dep),
"extra": Provide(router_dep),
},
)
async def item(
item_id: int,
token: str,
extra: str = Provides(),
*,
_router_dep=Provides(),
_route_dep=Provides(),
):
return {"id": item_id, "token": token, "extra": extra}
app.include(path="/v1", app=router, dependencies={"_include_dep": Provide(include_dep)})
Operational Guidance¶
- Run
map rules --source SOURCE_KEYbefore conversion. - Run
map applied REPORT.jsonafter conversion to audit triggered rules. - Treat diagnostics as migration checklist items.